dipping-charts 0.1.0

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 (137) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -0
  3. package/dist/__tests__/FullFeaturedChart.test.d.ts +2 -0
  4. package/dist/__tests__/FullFeaturedChart.test.d.ts.map +1 -0
  5. package/dist/__tests__/indicators-accuracy.test.d.ts +2 -0
  6. package/dist/__tests__/indicators-accuracy.test.d.ts.map +1 -0
  7. package/dist/__tests__/indicators.test.d.ts +2 -0
  8. package/dist/__tests__/indicators.test.d.ts.map +1 -0
  9. package/dist/__tests__/setup.d.ts +1 -0
  10. package/dist/__tests__/setup.d.ts.map +1 -0
  11. package/dist/__tests__/validateCandle.test.d.ts +2 -0
  12. package/dist/__tests__/validateCandle.test.d.ts.map +1 -0
  13. package/dist/chart/index.d.ts +2 -0
  14. package/dist/chart/index.js +5 -0
  15. package/dist/chart/index.js.map +1 -0
  16. package/dist/components/TradingChart.d.ts +24 -0
  17. package/dist/components/TradingChart.d.ts.map +1 -0
  18. package/dist/components/TradingChart.js +100 -0
  19. package/dist/components/TradingChart.js.map +1 -0
  20. package/dist/components/index.d.ts +3 -0
  21. package/dist/components/index.d.ts.map +1 -0
  22. package/dist/dipping-charts.css +1 -0
  23. package/dist/index.d.ts +5 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +28 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/indicators/atr.d.ts +15 -0
  28. package/dist/indicators/atr.d.ts.map +1 -0
  29. package/dist/indicators/atr.js +30 -0
  30. package/dist/indicators/atr.js.map +1 -0
  31. package/dist/indicators/bollingerBands.d.ts +11 -0
  32. package/dist/indicators/bollingerBands.d.ts.map +1 -0
  33. package/dist/indicators/bollingerBands.js +39 -0
  34. package/dist/indicators/bollingerBands.js.map +1 -0
  35. package/dist/indicators/currencyStrength.d.ts +43 -0
  36. package/dist/indicators/currencyStrength.d.ts.map +1 -0
  37. package/dist/indicators/currencyStrength.js +53 -0
  38. package/dist/indicators/currencyStrength.js.map +1 -0
  39. package/dist/indicators/ema.d.ts +11 -0
  40. package/dist/indicators/ema.d.ts.map +1 -0
  41. package/dist/indicators/ema.js +24 -0
  42. package/dist/indicators/ema.js.map +1 -0
  43. package/dist/indicators/index.d.ts +19 -0
  44. package/dist/indicators/index.d.ts.map +1 -0
  45. package/dist/indicators/index.js +23 -0
  46. package/dist/indicators/index.js.map +1 -0
  47. package/dist/indicators/macd.d.ts +11 -0
  48. package/dist/indicators/macd.d.ts.map +1 -0
  49. package/dist/indicators/macd.js +52 -0
  50. package/dist/indicators/macd.js.map +1 -0
  51. package/dist/indicators/rsi.d.ts +11 -0
  52. package/dist/indicators/rsi.d.ts.map +1 -0
  53. package/dist/indicators/rsi.js +29 -0
  54. package/dist/indicators/rsi.js.map +1 -0
  55. package/dist/indicators/sma.d.ts +13 -0
  56. package/dist/indicators/sma.d.ts.map +1 -0
  57. package/dist/indicators/sma.js +22 -0
  58. package/dist/indicators/sma.js.map +1 -0
  59. package/dist/indicators/stochastic.d.ts +15 -0
  60. package/dist/indicators/stochastic.d.ts.map +1 -0
  61. package/dist/indicators/stochastic.js +34 -0
  62. package/dist/indicators/stochastic.js.map +1 -0
  63. package/dist/indicators/types.d.ts +102 -0
  64. package/dist/indicators/types.d.ts.map +1 -0
  65. package/dist/indicators/vwap.d.ts +14 -0
  66. package/dist/indicators/vwap.d.ts.map +1 -0
  67. package/dist/indicators/vwap.js +17 -0
  68. package/dist/indicators/vwap.js.map +1 -0
  69. package/dist/indicators/williamsR.d.ts +17 -0
  70. package/dist/indicators/williamsR.d.ts.map +1 -0
  71. package/dist/indicators/williamsR.js +19 -0
  72. package/dist/indicators/williamsR.js.map +1 -0
  73. package/dist/react/FullFeaturedChart.d.ts +3 -0
  74. package/dist/react/FullFeaturedChart.d.ts.map +1 -0
  75. package/dist/react/FullFeaturedChart.js +640 -0
  76. package/dist/react/FullFeaturedChart.js.map +1 -0
  77. package/dist/react/components/IndicatorSettings.d.ts +20 -0
  78. package/dist/react/components/IndicatorSettings.d.ts.map +1 -0
  79. package/dist/react/components/IndicatorSettings.js +748 -0
  80. package/dist/react/components/IndicatorSettings.js.map +1 -0
  81. package/dist/react/hooks/useChart.d.ts +15 -0
  82. package/dist/react/hooks/useChart.d.ts.map +1 -0
  83. package/dist/react/hooks/useChart.js +155 -0
  84. package/dist/react/hooks/useChart.js.map +1 -0
  85. package/dist/react/hooks/useIndicators.d.ts +10 -0
  86. package/dist/react/hooks/useIndicators.d.ts.map +1 -0
  87. package/dist/react/hooks/useIndicators.js +264 -0
  88. package/dist/react/hooks/useIndicators.js.map +1 -0
  89. package/dist/react/hooks/useLineTools.d.ts +26 -0
  90. package/dist/react/hooks/useLineTools.d.ts.map +1 -0
  91. package/dist/react/hooks/useLineTools.js +189 -0
  92. package/dist/react/hooks/useLineTools.js.map +1 -0
  93. package/dist/react/hooks/useShiftSnap.d.ts +12 -0
  94. package/dist/react/hooks/useShiftSnap.d.ts.map +1 -0
  95. package/dist/react/hooks/useShiftSnap.js +54 -0
  96. package/dist/react/hooks/useShiftSnap.js.map +1 -0
  97. package/dist/react/index.d.ts +14 -0
  98. package/dist/react/index.d.ts.map +1 -0
  99. package/dist/react/index.js +18 -0
  100. package/dist/react/index.js.map +1 -0
  101. package/dist/react/loadLightweightCharts.d.ts +18 -0
  102. package/dist/react/loadLightweightCharts.d.ts.map +1 -0
  103. package/dist/react/loadLightweightCharts.js +32 -0
  104. package/dist/react/loadLightweightCharts.js.map +1 -0
  105. package/dist/react/locale.d.ts +79 -0
  106. package/dist/react/locale.d.ts.map +1 -0
  107. package/dist/react/locale.js +158 -0
  108. package/dist/react/locale.js.map +1 -0
  109. package/dist/react/types.d.ts +130 -0
  110. package/dist/react/types.d.ts.map +1 -0
  111. package/dist/types/index.d.ts +24 -0
  112. package/dist/types/index.d.ts.map +1 -0
  113. package/dist/utils/getToolId.d.ts +9 -0
  114. package/dist/utils/getToolId.d.ts.map +1 -0
  115. package/dist/utils/getToolId.js +12 -0
  116. package/dist/utils/getToolId.js.map +1 -0
  117. package/dist/utils/mockData.d.ts +10 -0
  118. package/dist/utils/mockData.d.ts.map +1 -0
  119. package/dist/utils/mockData.js +61 -0
  120. package/dist/utils/mockData.js.map +1 -0
  121. package/dist/utils/snapCrosshair.d.ts +25 -0
  122. package/dist/utils/snapCrosshair.d.ts.map +1 -0
  123. package/dist/utils/validateCandle.d.ts +30 -0
  124. package/dist/utils/validateCandle.d.ts.map +1 -0
  125. package/dist/utils/validateCandle.js +21 -0
  126. package/dist/utils/validateCandle.js.map +1 -0
  127. package/examples/css/base.css +209 -0
  128. package/examples/css/chart.css +282 -0
  129. package/examples/css/indicators.css +255 -0
  130. package/examples/index.html +163 -0
  131. package/examples/js/chart.js +370 -0
  132. package/examples/js/indicators.js +27 -0
  133. package/examples/js/main.js +6 -0
  134. package/examples/js/ui.js +1641 -0
  135. package/lib/lightweight-charts.standalone.production.js +7 -0
  136. package/package.json +106 -0
  137. package/src/react/FullFeaturedChart.css +1007 -0
@@ -0,0 +1,1007 @@
1
+ @import url('https://cdn.jsdelivr.net/gh/toss/tossface/dist/tossface.css');
2
+
3
+ .tossface {
4
+ font-family: Tossface;
5
+ }
6
+ .container *,
7
+ .container *::before,
8
+ .container *::after {
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ .container {
13
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
14
+ background: white;
15
+ border-radius: 8px;
16
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
17
+ overflow: hidden;
18
+ }
19
+
20
+ .header {
21
+ padding: 8px 12px;
22
+ border-bottom: 1px solid #e0e0e0;
23
+ background: white;
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: space-between;
27
+ flex-shrink: 0; /* 헤더가 줄어들지 않도록 */
28
+ }
29
+
30
+ .header-left {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: 8px;
34
+ }
35
+
36
+ .header-right {
37
+ display: flex;
38
+ align-items: center;
39
+ gap: 6px;
40
+ }
41
+
42
+ .timeframe-group {
43
+ display: flex;
44
+ gap: 2px;
45
+ background: #f5f5f5;
46
+ padding: 3px;
47
+ border-radius: 6px;
48
+ }
49
+
50
+ .btn-timeframe {
51
+ padding: 4px 10px;
52
+ border: none;
53
+ background: transparent;
54
+ border-radius: 4px;
55
+ cursor: pointer;
56
+ font-size: 13px;
57
+ font-weight: 500;
58
+ color: #666;
59
+ transition: all 0.15s;
60
+ }
61
+
62
+ .btn-timeframe:hover {
63
+ background: #e8e8e8;
64
+ color: #333;
65
+ }
66
+
67
+ .btn-timeframe.active {
68
+ background: white;
69
+ color: #333;
70
+ box-shadow: 0 1px 2px rgba(0,0,0,0.1);
71
+ }
72
+
73
+ .btn-icon {
74
+ width: 32px;
75
+ height: 32px;
76
+ border: none;
77
+ background: transparent;
78
+ border-radius: 4px;
79
+ cursor: pointer;
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: center;
83
+ color: #666;
84
+ font-size: 16px;
85
+ transition: all 0.15s;
86
+ }
87
+
88
+ .btn-icon:hover {
89
+ background: #f5f5f5;
90
+ color: #333;
91
+ }
92
+
93
+ .btn-icon.active {
94
+ background: #e8f2ff;
95
+ color: #2563eb;
96
+ }
97
+
98
+ .separator {
99
+ width: 1px;
100
+ height: 20px;
101
+ background: #e0e0e0;
102
+ margin: 0 4px;
103
+ }
104
+
105
+ .btn-text {
106
+ padding: 6px 12px;
107
+ border: none;
108
+ background: transparent;
109
+ border-radius: 4px;
110
+ cursor: pointer;
111
+ font-size: 14px;
112
+ font-weight: 500;
113
+ color: #666;
114
+ transition: all 0.15s;
115
+ display: flex;
116
+ align-items: center;
117
+ gap: 4px;
118
+ }
119
+
120
+ .btn-text:hover {
121
+ background: #f5f5f5;
122
+ color: #333;
123
+ }
124
+
125
+ .btn-text.active {
126
+ background: #e8f2ff;
127
+ color: #2563eb;
128
+ }
129
+
130
+ .btn-delete {
131
+ padding: 6px 12px;
132
+ border: none;
133
+ background: #fee;
134
+ border-radius: 4px;
135
+ cursor: pointer;
136
+ font-size: 14px;
137
+ font-weight: 500;
138
+ color: #dc2626;
139
+ transition: all 0.15s;
140
+ }
141
+
142
+ .btn-delete:hover {
143
+ background: #fdd;
144
+ }
145
+
146
+ /* 드롭다운 메뉴 */
147
+ .dropdown {
148
+ position: relative;
149
+ }
150
+
151
+ .dropdown-menu {
152
+ position: absolute;
153
+ top: calc(100% + 4px);
154
+ right: 0;
155
+ background: white;
156
+ border: 1px solid #e0e0e0;
157
+ border-radius: 12px;
158
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
159
+ min-width: 200px;
160
+ padding: 8px;
161
+ display: none;
162
+ z-index: 1000;
163
+ }
164
+
165
+ .dropdown-menu.show {
166
+ display: block;
167
+ animation: fadeInDown 0.15s ease-out;
168
+ }
169
+
170
+ #indicatorMenu.show {
171
+ display: flex;
172
+ animation: fadeInDown 0.15s ease-out;
173
+ }
174
+
175
+ @keyframes fadeInDown {
176
+ from { opacity: 0; transform: translateY(-8px); }
177
+ to { opacity: 1; transform: translateY(0); }
178
+ }
179
+
180
+ .dropdown-item {
181
+ width: 100%;
182
+ padding: 8px 12px;
183
+ border: none;
184
+ background: transparent;
185
+ border-radius: 4px;
186
+ cursor: pointer;
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 8px;
190
+ font-size: 13px;
191
+ color: #333;
192
+ transition: all 0.15s;
193
+ text-align: left;
194
+ }
195
+
196
+ .dropdown-item:hover {
197
+ background: #f5f5f5;
198
+ }
199
+
200
+ .dropdown-item.active {
201
+ background: #e8f2ff;
202
+ color: #2563eb;
203
+ }
204
+
205
+ .item-icon {
206
+ font-size: 16px;
207
+ width: 20px;
208
+ text-align: center;
209
+ }
210
+
211
+ .modal-overlay {
212
+ position: fixed;
213
+ top: 0;
214
+ left: 0;
215
+ right: 0;
216
+ bottom: 0;
217
+ background: rgba(0, 0, 0, 0.5);
218
+ display: none;
219
+ align-items: center;
220
+ justify-content: center;
221
+ z-index: 10000;
222
+ }
223
+
224
+ .modal-overlay.show {
225
+ display: flex;
226
+ animation: fadeIn 0.15s ease-out;
227
+ }
228
+
229
+ @keyframes fadeIn {
230
+ from { opacity: 0; }
231
+ to { opacity: 1; }
232
+ }
233
+
234
+ .modal-content {
235
+ background: white;
236
+ border-radius: 8px;
237
+ padding: 20px;
238
+ min-width: 300px;
239
+ box-shadow: 0 8px 24px rgba(0,0,0,0.2);
240
+ }
241
+
242
+ .modal-title {
243
+ font-size: 16px;
244
+ font-weight: 600;
245
+ margin-bottom: 12px;
246
+ color: #333;
247
+ }
248
+
249
+ .modal-input {
250
+ width: 100%;
251
+ padding: 8px 12px;
252
+ border: 1px solid #ddd;
253
+ border-radius: 6px;
254
+ font-size: 14px;
255
+ margin-bottom: 16px;
256
+ box-sizing: border-box;
257
+ }
258
+
259
+ .modal-input:focus {
260
+ outline: none;
261
+ border-color: #2563eb;
262
+ }
263
+
264
+ .modal-buttons {
265
+ display: flex;
266
+ gap: 8px;
267
+ justify-content: flex-end;
268
+ }
269
+
270
+ .modal-btn {
271
+ padding: 8px 16px;
272
+ border: none;
273
+ border-radius: 6px;
274
+ font-size: 13px;
275
+ font-weight: 500;
276
+ cursor: pointer;
277
+ transition: all 0.15s;
278
+ }
279
+
280
+ .modal-btn-cancel {
281
+ background: #f5f5f5;
282
+ color: #666;
283
+ }
284
+
285
+ .modal-btn-cancel:hover {
286
+ background: #e8e8e8;
287
+ }
288
+
289
+ .modal-btn-confirm {
290
+ background: #2563eb;
291
+ color: white;
292
+ }
293
+
294
+ .modal-btn-confirm:hover {
295
+ background: #1d4ed8;
296
+ }
297
+
298
+ #chart {
299
+ width: 100%;
300
+ height: 600px;
301
+ position: relative;
302
+ }
303
+
304
+ .info {
305
+ padding: 20px;
306
+ border-top: 1px solid #e5e5e5;
307
+ background: #fafafa;
308
+ }
309
+
310
+ .info h3 {
311
+ font-size: 16px;
312
+ margin-bottom: 10px;
313
+ color: #666;
314
+ }
315
+
316
+ .info ul {
317
+ list-style: none;
318
+ padding-left: 0;
319
+ }
320
+
321
+ .info li {
322
+ padding: 4px 0;
323
+ color: #666;
324
+ font-size: 14px;
325
+ }
326
+
327
+ /* Context Menu Styles - Toolbar Style (Light Mode) */
328
+ .context-menu {
329
+ position: absolute;
330
+ background: rgba(255, 255, 255, 0.95);
331
+ backdrop-filter: blur(10px);
332
+ border: 1px solid rgba(0, 0, 0, 0.1);
333
+ border-radius: 8px;
334
+ box-shadow: 0 4px 20px rgba(0,0,0,0.15);
335
+ padding: 6px;
336
+ z-index: 10000;
337
+ display: none;
338
+ gap: 4px;
339
+ flex-direction: row;
340
+ align-items: center;
341
+ cursor: move;
342
+ user-select: none;
343
+ }
344
+
345
+ .context-menu.show {
346
+ display: flex;
347
+ animation: fadeInScale 0.15s ease-out;
348
+ }
349
+
350
+ @keyframes fadeInScale {
351
+ from { opacity: 0; transform: scale(0.95); }
352
+ to { opacity: 1; transform: scale(1); }
353
+ }
354
+
355
+ .context-menu.dragging {
356
+ cursor: grabbing;
357
+ }
358
+
359
+ .context-menu-btn {
360
+ width: 36px;
361
+ height: 36px;
362
+ border: none;
363
+ background: transparent;
364
+ color: #333;
365
+ cursor: pointer;
366
+ border-radius: 6px;
367
+ display: flex;
368
+ align-items: center;
369
+ justify-content: center;
370
+ font-size: 18px;
371
+ transition: all 0.2s;
372
+ position: relative;
373
+ }
374
+
375
+ .context-menu-btn:hover {
376
+ background: rgba(0, 0, 0, 0.05);
377
+ }
378
+
379
+ .context-menu-btn.active {
380
+ background: rgba(59, 130, 246, 0.15);
381
+ color: #2563eb;
382
+ }
383
+
384
+ .context-menu-btn.danger:hover {
385
+ background: rgba(239, 68, 68, 0.1);
386
+ color: #dc2626;
387
+ }
388
+
389
+ .context-menu-separator {
390
+ width: 1px;
391
+ height: 24px;
392
+ background: rgba(0, 0, 0, 0.1);
393
+ margin: 0 4px;
394
+ }
395
+
396
+ /* 선 두께 표시 */
397
+ .width-display {
398
+ min-width: 32px;
399
+ height: 36px;
400
+ display: flex;
401
+ align-items: center;
402
+ justify-content: center;
403
+ font-size: 16px;
404
+ font-weight: bold;
405
+ color: #333;
406
+ background: rgba(0, 0, 0, 0.05);
407
+ border-radius: 6px;
408
+ padding: 0 8px;
409
+ user-select: none;
410
+ }
411
+
412
+ /* 색상 드롭다운 */
413
+ .color-picker-dropdown {
414
+ position: relative;
415
+ }
416
+
417
+ .color-current-btn {
418
+ width: 36px;
419
+ height: 36px;
420
+ border: none;
421
+ background: transparent;
422
+ cursor: pointer;
423
+ border-radius: 6px;
424
+ display: flex;
425
+ align-items: center;
426
+ justify-content: center;
427
+ transition: all 0.2s;
428
+ position: relative;
429
+ padding: 0;
430
+ }
431
+
432
+ .color-current-btn:hover {
433
+ background: rgba(0, 0, 0, 0.05);
434
+ }
435
+
436
+ .color-current-display {
437
+ width: 24px;
438
+ height: 24px;
439
+ border-radius: 4px;
440
+ border: 2px solid rgba(0, 0, 0, 0.2);
441
+ }
442
+
443
+ .color-palette {
444
+ position: absolute;
445
+ top: 100%;
446
+ left: 50%;
447
+ transform: translateX(-50%);
448
+ margin-top: 4px;
449
+ background: rgba(255, 255, 255, 0.95);
450
+ backdrop-filter: blur(10px);
451
+ border: 1px solid rgba(0, 0, 0, 0.1);
452
+ border-radius: 8px;
453
+ padding: 8px;
454
+ display: none;
455
+ gap: 6px;
456
+ flex-wrap: wrap;
457
+ width: 120px;
458
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
459
+ z-index: 10002;
460
+ }
461
+
462
+ .color-palette.show {
463
+ display: flex;
464
+ animation: fadeInDown 0.15s ease-out;
465
+ }
466
+
467
+ .color-option {
468
+ width: 28px;
469
+ height: 28px;
470
+ border: 2px solid transparent;
471
+ border-radius: 6px;
472
+ cursor: pointer;
473
+ transition: all 0.2s;
474
+ padding: 0;
475
+ }
476
+
477
+ .color-option:hover {
478
+ transform: scale(1.1);
479
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
480
+ }
481
+
482
+ /* Tooltip */
483
+ .context-menu-btn::after,
484
+ .color-current-btn::after {
485
+ content: attr(data-tooltip);
486
+ position: absolute;
487
+ bottom: -32px;
488
+ left: 50%;
489
+ transform: translateX(-50%);
490
+ background: rgba(0, 0, 0, 0.85);
491
+ color: white;
492
+ padding: 4px 8px;
493
+ border-radius: 4px;
494
+ font-size: 11px;
495
+ white-space: nowrap;
496
+ opacity: 0;
497
+ pointer-events: none;
498
+ transition: opacity 0.2s;
499
+ z-index: 10001;
500
+ }
501
+
502
+ .context-menu-btn:hover::after,
503
+ .color-current-btn:hover::after {
504
+ opacity: 1;
505
+ }
506
+ /* 보조지표 드롭다운 (넓은 버전) */
507
+ #indicatorMenu {
508
+ width: 600px;
509
+ padding: 0;
510
+ max-height: 500px;
511
+ }
512
+
513
+ .indicator-menu-layout {
514
+ display: flex;
515
+ width: 100%;
516
+ height: 100%;
517
+ overflow: hidden;
518
+ border-radius: 12px;
519
+ }
520
+
521
+ .indicator-list-side {
522
+ width: 180px;
523
+ background: #f8f9fa;
524
+ border-right: 1px solid #e0e0e0;
525
+ overflow-y: auto;
526
+ max-height: 500px;
527
+ border-radius: 12px 0 0 12px;
528
+ }
529
+
530
+ .indicator-category {
531
+ padding: 12px 16px 8px;
532
+ font-size: 11px;
533
+ font-weight: 600;
534
+ color: #999;
535
+ }
536
+
537
+ .indicator-item {
538
+ display: flex;
539
+ align-items: center;
540
+ justify-content: space-between;
541
+ padding: 10px 16px;
542
+ cursor: pointer;
543
+ font-size: 13px;
544
+ color: #333;
545
+ transition: all 0.15s;
546
+ }
547
+
548
+ .indicator-item:hover {
549
+ background: rgba(0,0,0,0.03);
550
+ }
551
+
552
+ .indicator-item.selected {
553
+ background: white;
554
+ color: #2563eb;
555
+ border-left: 3px solid #2563eb;
556
+ }
557
+
558
+ .indicator-checkbox {
559
+ width: 20px;
560
+ height: 20px;
561
+ border: 2px solid #d0d0d0;
562
+ border-radius: 50%;
563
+ display: flex;
564
+ align-items: center;
565
+ justify-content: center;
566
+ transition: all 0.15s;
567
+ cursor: pointer;
568
+ padding: 4px; /* 클릭 영역 확대 */
569
+ }
570
+
571
+ .indicator-checkbox:hover {
572
+ border-color: #2563eb;
573
+ background: #f0f5ff;
574
+ }
575
+
576
+ .indicator-item.checked .indicator-checkbox {
577
+ background: #2563eb;
578
+ border-color: #2563eb;
579
+ }
580
+
581
+ .indicator-item.checked .indicator-checkbox::after {
582
+ content: '✓';
583
+ color: white;
584
+ font-size: 12px;
585
+ font-weight: bold;
586
+ }
587
+
588
+ .indicator-settings-side {
589
+ flex: 1;
590
+ padding: 16px;
591
+ overflow-y: auto;
592
+ max-height: 500px;
593
+ border-radius: 0 12px 12px 0;
594
+ }
595
+
596
+ .indicator-settings-title {
597
+ font-size: 14px;
598
+ font-weight: 600;
599
+ margin-bottom: 4px;
600
+ }
601
+
602
+ .indicator-settings-desc {
603
+ font-size: 12px;
604
+ color: #666;
605
+ margin-bottom: 16px;
606
+ }
607
+
608
+ .indicator-period-row {
609
+ display: flex;
610
+ align-items: center;
611
+ gap: 8px;
612
+ margin-bottom: 12px;
613
+ }
614
+
615
+ .period-label-col {
616
+ min-width: 60px;
617
+ font-size: 13px;
618
+ color: #666;
619
+ }
620
+
621
+ .period-color-picker {
622
+ width: 60px;
623
+ height: 36px;
624
+ border: 2px solid #e0e0e0;
625
+ border-radius: 6px;
626
+ cursor: pointer;
627
+ transition: all 0.15s;
628
+ }
629
+
630
+ .period-color-picker:hover {
631
+ border-color: #999;
632
+ }
633
+
634
+ .color-palette-popup {
635
+ position: fixed;
636
+ background: white;
637
+ border: 1px solid #e0e0e0;
638
+ border-radius: 8px;
639
+ padding: 12px 20px 12px 12px;
640
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
641
+ display: none;
642
+ z-index: 10000;
643
+ width: 280px;
644
+ }
645
+
646
+ .color-palette-popup.show {
647
+ display: block;
648
+ animation: fadeIn 0.15s ease-out;
649
+ }
650
+
651
+ .color-palette-title {
652
+ font-size: 12px;
653
+ font-weight: 600;
654
+ color: #333;
655
+ margin-bottom: 8px;
656
+ }
657
+
658
+ .color-palette-grid {
659
+ display: grid;
660
+ grid-template-columns: repeat(8, 1fr);
661
+ gap: 4px;
662
+ }
663
+
664
+ .color-palette-item {
665
+ width: 28px;
666
+ height: 28px;
667
+ border-radius: 50%;
668
+ cursor: pointer;
669
+ border: 2px solid transparent;
670
+ transition: all 0.15s;
671
+ }
672
+
673
+ .color-palette-item:hover {
674
+ transform: scale(1.1);
675
+ border-color: #333;
676
+ }
677
+
678
+ .color-palette-item.selected {
679
+ border-color: #2563eb;
680
+ box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
681
+ }
682
+
683
+ .period-thickness-display {
684
+ width: 60px;
685
+ height: 36px;
686
+ border: 1px solid #e0e0e0;
687
+ border-radius: 6px;
688
+ display: flex;
689
+ align-items: center;
690
+ justify-content: center;
691
+ font-size: 13px;
692
+ background: white;
693
+ cursor: pointer;
694
+ }
695
+
696
+ .period-source-dropdown {
697
+ width: 90px;
698
+ height: 36px;
699
+ padding: 0 8px;
700
+ border: 1px solid #e0e0e0;
701
+ border-radius: 6px;
702
+ font-size: 13px;
703
+ background: white;
704
+ cursor: pointer;
705
+ }
706
+
707
+ .period-value-field {
708
+ width: 80px;
709
+ height: 36px;
710
+ padding: 0 8px;
711
+ border: 1px solid #e0e0e0;
712
+ border-radius: 6px;
713
+ font-size: 13px;
714
+ text-align: center;
715
+ }
716
+
717
+ .period-delete-btn {
718
+ width: 32px;
719
+ height: 32px;
720
+ border: none;
721
+ background: transparent;
722
+ border-radius: 6px;
723
+ cursor: pointer;
724
+ font-size: 18px;
725
+ color: #999;
726
+ transition: all 0.15s;
727
+ }
728
+
729
+ .period-delete-btn:hover {
730
+ background: rgba(239, 68, 68, 0.1);
731
+ color: #dc2626;
732
+ }
733
+
734
+ .add-period-button {
735
+ width: 100%;
736
+ padding: 10px;
737
+ border: 2px dashed #e0e0e0;
738
+ background: transparent;
739
+ border-radius: 6px;
740
+ cursor: pointer;
741
+ font-size: 13px;
742
+ color: #666;
743
+ display: flex;
744
+ align-items: center;
745
+ justify-content: center;
746
+ gap: 6px;
747
+ transition: all 0.15s;
748
+ margin-top: 8px;
749
+ }
750
+
751
+ .add-period-button:hover {
752
+ border-color: #2563eb;
753
+ color: #2563eb;
754
+ }
755
+
756
+ .indicator-empty-state {
757
+ text-align: center;
758
+ padding: 40px 20px;
759
+ color: #999;
760
+ font-size: 13px;
761
+ }
762
+
763
+
764
+ /* Disabled 타임프레임 버튼 */
765
+ .btn-timeframe.disabled {
766
+ color: #ccc;
767
+ cursor: not-allowed;
768
+ opacity: 0.5;
769
+ }
770
+
771
+ .btn-timeframe.disabled:hover {
772
+ background: transparent;
773
+ color: #ccc;
774
+ }
775
+
776
+ .chart-symbol {
777
+ font-size: 14px;
778
+ font-weight: 600;
779
+ color: #333333;
780
+ margin-left: 12px;
781
+ }
782
+
783
+ .chart-overlay {
784
+
785
+ position: absolute;
786
+ top: 0;
787
+ left: 0;
788
+ right: 0;
789
+ bottom: 0;
790
+ display: flex;
791
+ flex-direction: column;
792
+ align-items: center;
793
+ justify-content: center;
794
+ gap: 12px;
795
+ background: rgba(255, 255, 255, 0.9);
796
+ z-index: 10;
797
+ }
798
+
799
+ .loading-overlay .loading-spinner {
800
+ width: 32px;
801
+ height: 32px;
802
+ border: 3px solid #f3f3f3;
803
+ border-top: 3px solid #3b82f6;
804
+ border-radius: 50%;
805
+ animation: spin 1s linear infinite;
806
+ }
807
+
808
+ @keyframes spin {
809
+ 0% { transform: rotate(0deg); }
810
+ 100% { transform: rotate(360deg); }
811
+ }
812
+
813
+ .loading-overlay span,
814
+ .empty-overlay span {
815
+ color: #666;
816
+ font-size: 14px;
817
+ }
818
+
819
+ .error-overlay {
820
+ background: rgba(254, 242, 242, 0.95);
821
+ }
822
+
823
+ .error-overlay .error-icon {
824
+ font-size: 24px;
825
+ }
826
+
827
+ .error-overlay span {
828
+ color: #dc2626;
829
+ font-size: 14px;
830
+ }
831
+
832
+ /* 활성 그리기 도구 배지 */
833
+ .active-tool-badge {
834
+ padding: 4px 10px;
835
+ background: #e8f2ff;
836
+ color: #2563eb;
837
+ font-size: 12px;
838
+ font-weight: 600;
839
+ border-radius: 12px;
840
+ animation: fadeIn 0.15s ease-out;
841
+ white-space: nowrap;
842
+ }
843
+
844
+ /* ====== 반응형 디자인 ====== */
845
+
846
+ /* 태블릿 가로 (1024px) */
847
+ @media (max-width: 1024px) {
848
+ #indicatorMenu {
849
+ width: 480px;
850
+ }
851
+
852
+ .indicator-list-side {
853
+ width: 140px;
854
+ }
855
+ }
856
+
857
+ /* 태블릿 세로 (768px) */
858
+ @media (max-width: 768px) {
859
+ .header {
860
+ padding: 6px 8px;
861
+ flex-wrap: wrap;
862
+ gap: 6px;
863
+ }
864
+
865
+ .header-left {
866
+ width: 100%;
867
+ overflow-x: auto;
868
+ -webkit-overflow-scrolling: touch;
869
+ }
870
+
871
+ .header-right {
872
+ width: 100%;
873
+ justify-content: flex-end;
874
+ flex-wrap: wrap;
875
+ }
876
+
877
+ .timeframe-group {
878
+ flex-shrink: 0;
879
+ }
880
+
881
+ .btn-timeframe {
882
+ padding: 4px 8px;
883
+ font-size: 12px;
884
+ }
885
+
886
+ .btn-text {
887
+ padding: 4px 8px;
888
+ font-size: 13px;
889
+ }
890
+
891
+ .btn-delete {
892
+ padding: 4px 8px;
893
+ font-size: 13px;
894
+ }
895
+
896
+ #indicatorMenu {
897
+ width: calc(100vw - 32px);
898
+ max-width: 480px;
899
+ right: -8px;
900
+ }
901
+
902
+ .indicator-list-side {
903
+ width: 120px;
904
+ }
905
+
906
+ .indicator-settings-side {
907
+ padding: 12px;
908
+ }
909
+
910
+ .modal-content {
911
+ min-width: auto;
912
+ width: calc(100vw - 40px);
913
+ max-width: 360px;
914
+ }
915
+ }
916
+
917
+ /* 모바일 (480px) */
918
+ @media (max-width: 480px) {
919
+ .header {
920
+ padding: 4px 6px;
921
+ }
922
+
923
+ .btn-timeframe {
924
+ padding: 4px 6px;
925
+ font-size: 11px;
926
+ }
927
+
928
+ .btn-text {
929
+ padding: 4px 6px;
930
+ font-size: 12px;
931
+ }
932
+
933
+ .btn-delete {
934
+ padding: 4px 6px;
935
+ font-size: 12px;
936
+ }
937
+
938
+ .active-tool-badge {
939
+ font-size: 11px;
940
+ padding: 2px 8px;
941
+ }
942
+
943
+ #indicatorMenu {
944
+ position: fixed;
945
+ top: auto;
946
+ bottom: 0;
947
+ left: 0;
948
+ right: 0;
949
+ width: 100%;
950
+ max-width: 100%;
951
+ max-height: 70vh;
952
+ border-radius: 12px 12px 0 0;
953
+ }
954
+
955
+ .indicator-menu-layout {
956
+ flex-direction: column;
957
+ border-radius: 12px 12px 0 0;
958
+ }
959
+
960
+ .indicator-list-side {
961
+ width: 100%;
962
+ max-height: 160px;
963
+ border-right: none;
964
+ border-bottom: 1px solid #e0e0e0;
965
+ border-radius: 12px 12px 0 0;
966
+ display: flex;
967
+ flex-wrap: wrap;
968
+ align-content: flex-start;
969
+ }
970
+
971
+ .indicator-category {
972
+ width: 100%;
973
+ padding: 8px 12px 4px;
974
+ }
975
+
976
+ .indicator-item {
977
+ padding: 8px 12px;
978
+ }
979
+
980
+ .indicator-settings-side {
981
+ border-radius: 0;
982
+ max-height: calc(70vh - 160px);
983
+ }
984
+
985
+ .context-menu {
986
+ padding: 4px;
987
+ }
988
+
989
+ .context-menu-btn {
990
+ width: 32px;
991
+ height: 32px;
992
+ font-size: 16px;
993
+ }
994
+
995
+ .width-display {
996
+ min-width: 28px;
997
+ height: 32px;
998
+ font-size: 14px;
999
+ }
1000
+
1001
+ .dropdown-menu:not(#indicatorMenu) {
1002
+ right: -6px;
1003
+ min-width: 160px;
1004
+ }
1005
+ }
1006
+
1007
+ /* 커스텀 텍스트 입력 모달 */