suneditor 3.0.0-beta.2 → 3.0.0-beta.20

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 (177) hide show
  1. package/CONTRIBUTING.md +186 -184
  2. package/LICENSE +21 -21
  3. package/README.md +157 -180
  4. package/dist/suneditor.min.css +1 -1
  5. package/dist/suneditor.min.js +1 -1
  6. package/package.json +126 -123
  7. package/src/assets/design/color.css +131 -121
  8. package/src/assets/design/index.css +3 -3
  9. package/src/assets/design/size.css +37 -35
  10. package/src/assets/design/typography.css +37 -37
  11. package/src/assets/icons/defaultIcons.js +247 -232
  12. package/src/assets/suneditor-contents.css +779 -778
  13. package/src/assets/suneditor.css +43 -35
  14. package/src/core/base/eventHandlers/handler_toolbar.js +135 -135
  15. package/src/core/base/eventHandlers/handler_ww_clipboard.js +56 -56
  16. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +115 -113
  17. package/src/core/base/eventHandlers/handler_ww_key_input.js +1200 -1200
  18. package/src/core/base/eventHandlers/handler_ww_mouse.js +194 -194
  19. package/src/core/base/eventManager.js +1550 -1484
  20. package/src/core/base/history.js +355 -355
  21. package/src/core/class/char.js +163 -162
  22. package/src/core/class/component.js +856 -842
  23. package/src/core/class/format.js +3433 -3422
  24. package/src/core/class/html.js +1927 -1890
  25. package/src/core/class/menu.js +357 -346
  26. package/src/core/class/nodeTransform.js +424 -424
  27. package/src/core/class/offset.js +858 -891
  28. package/src/core/class/selection.js +710 -620
  29. package/src/core/class/shortcuts.js +98 -98
  30. package/src/core/class/toolbar.js +438 -430
  31. package/src/core/class/ui.js +424 -422
  32. package/src/core/class/viewer.js +750 -750
  33. package/src/core/editor.js +1810 -1708
  34. package/src/core/section/actives.js +268 -241
  35. package/src/core/section/constructor.js +1348 -1661
  36. package/src/core/section/context.js +102 -102
  37. package/src/core/section/documentType.js +582 -561
  38. package/src/core/section/options.js +367 -0
  39. package/src/core/util/instanceCheck.js +59 -0
  40. package/src/editorInjector/_classes.js +36 -36
  41. package/src/editorInjector/_core.js +92 -92
  42. package/src/editorInjector/index.js +75 -75
  43. package/src/events.js +634 -622
  44. package/src/helper/clipboard.js +59 -59
  45. package/src/helper/converter.js +586 -564
  46. package/src/helper/dom/domCheck.js +304 -304
  47. package/src/helper/dom/domQuery.js +677 -669
  48. package/src/helper/dom/domUtils.js +618 -557
  49. package/src/helper/dom/index.js +12 -12
  50. package/src/helper/env.js +249 -240
  51. package/src/helper/index.js +25 -25
  52. package/src/helper/keyCodeMap.js +183 -183
  53. package/src/helper/numbers.js +72 -72
  54. package/src/helper/unicode.js +47 -47
  55. package/src/langs/ckb.js +231 -231
  56. package/src/langs/cs.js +231 -231
  57. package/src/langs/da.js +231 -231
  58. package/src/langs/de.js +231 -231
  59. package/src/langs/en.js +230 -230
  60. package/src/langs/es.js +231 -231
  61. package/src/langs/fa.js +231 -231
  62. package/src/langs/fr.js +231 -231
  63. package/src/langs/he.js +231 -231
  64. package/src/langs/hu.js +230 -230
  65. package/src/langs/index.js +28 -28
  66. package/src/langs/it.js +231 -231
  67. package/src/langs/ja.js +230 -230
  68. package/src/langs/km.js +230 -230
  69. package/src/langs/ko.js +230 -230
  70. package/src/langs/lv.js +231 -231
  71. package/src/langs/nl.js +231 -231
  72. package/src/langs/pl.js +231 -231
  73. package/src/langs/pt_br.js +231 -231
  74. package/src/langs/ro.js +231 -231
  75. package/src/langs/ru.js +231 -231
  76. package/src/langs/se.js +231 -231
  77. package/src/langs/tr.js +231 -231
  78. package/src/langs/uk.js +231 -231
  79. package/src/langs/ur.js +231 -231
  80. package/src/langs/zh_cn.js +231 -231
  81. package/src/modules/ApiManager.js +191 -191
  82. package/src/modules/Browser.js +669 -667
  83. package/src/modules/ColorPicker.js +364 -362
  84. package/src/modules/Controller.js +474 -454
  85. package/src/modules/Figure.js +1620 -1617
  86. package/src/modules/FileManager.js +359 -359
  87. package/src/modules/HueSlider.js +577 -565
  88. package/src/modules/Modal.js +346 -346
  89. package/src/modules/ModalAnchorEditor.js +643 -643
  90. package/src/modules/SelectMenu.js +549 -549
  91. package/src/modules/_DragHandle.js +17 -17
  92. package/src/modules/index.js +14 -14
  93. package/src/plugins/browser/audioGallery.js +83 -83
  94. package/src/plugins/browser/fileBrowser.js +103 -103
  95. package/src/plugins/browser/fileGallery.js +83 -83
  96. package/src/plugins/browser/imageGallery.js +81 -81
  97. package/src/plugins/browser/videoGallery.js +103 -103
  98. package/src/plugins/command/blockquote.js +61 -60
  99. package/src/plugins/command/exportPDF.js +134 -134
  100. package/src/plugins/command/fileUpload.js +456 -456
  101. package/src/plugins/command/list_bulleted.js +149 -148
  102. package/src/plugins/command/list_numbered.js +152 -151
  103. package/src/plugins/dropdown/align.js +157 -155
  104. package/src/plugins/dropdown/backgroundColor.js +108 -104
  105. package/src/plugins/dropdown/font.js +141 -137
  106. package/src/plugins/dropdown/fontColor.js +109 -105
  107. package/src/plugins/dropdown/formatBlock.js +170 -178
  108. package/src/plugins/dropdown/hr.js +152 -152
  109. package/src/plugins/dropdown/layout.js +83 -83
  110. package/src/plugins/dropdown/lineHeight.js +131 -130
  111. package/src/plugins/dropdown/list.js +123 -122
  112. package/src/plugins/dropdown/paragraphStyle.js +138 -138
  113. package/src/plugins/dropdown/table.js +4110 -4000
  114. package/src/plugins/dropdown/template.js +83 -83
  115. package/src/plugins/dropdown/textStyle.js +149 -149
  116. package/src/plugins/field/mention.js +242 -242
  117. package/src/plugins/index.js +120 -120
  118. package/src/plugins/input/fontSize.js +414 -410
  119. package/src/plugins/input/pageNavigator.js +71 -70
  120. package/src/plugins/modal/audio.js +677 -677
  121. package/src/plugins/modal/drawing.js +537 -531
  122. package/src/plugins/modal/embed.js +886 -886
  123. package/src/plugins/modal/image.js +1377 -1376
  124. package/src/plugins/modal/link.js +248 -240
  125. package/src/plugins/modal/math.js +563 -563
  126. package/src/plugins/modal/video.js +1226 -1226
  127. package/src/plugins/popup/anchor.js +224 -222
  128. package/src/suneditor.js +114 -107
  129. package/src/themes/dark.css +132 -122
  130. package/src/typedef.js +132 -130
  131. package/types/assets/icons/defaultIcons.d.ts +8 -0
  132. package/types/core/base/eventManager.d.ts +29 -4
  133. package/types/core/class/char.d.ts +2 -1
  134. package/types/core/class/component.d.ts +1 -2
  135. package/types/core/class/format.d.ts +8 -1
  136. package/types/core/class/html.d.ts +8 -0
  137. package/types/core/class/menu.d.ts +8 -0
  138. package/types/core/class/offset.d.ts +24 -26
  139. package/types/core/class/selection.d.ts +2 -0
  140. package/types/core/class/toolbar.d.ts +6 -0
  141. package/types/core/class/ui.d.ts +1 -1
  142. package/types/core/editor.d.ts +34 -12
  143. package/types/core/section/constructor.d.ts +5 -638
  144. package/types/core/section/documentType.d.ts +12 -2
  145. package/types/core/section/options.d.ts +740 -0
  146. package/types/core/util/instanceCheck.d.ts +50 -0
  147. package/types/editorInjector/_core.d.ts +5 -5
  148. package/types/editorInjector/index.d.ts +2 -2
  149. package/types/events.d.ts +2 -0
  150. package/types/helper/converter.d.ts +9 -0
  151. package/types/helper/dom/domQuery.d.ts +5 -5
  152. package/types/helper/dom/domUtils.d.ts +8 -0
  153. package/types/helper/env.d.ts +6 -1
  154. package/types/helper/index.d.ts +4 -1
  155. package/types/index.d.ts +122 -120
  156. package/types/langs/_Lang.d.ts +194 -194
  157. package/types/modules/ColorPicker.d.ts +5 -1
  158. package/types/modules/Controller.d.ts +8 -4
  159. package/types/modules/Figure.d.ts +2 -1
  160. package/types/modules/HueSlider.d.ts +4 -1
  161. package/types/modules/SelectMenu.d.ts +1 -1
  162. package/types/plugins/command/blockquote.d.ts +1 -0
  163. package/types/plugins/command/list_bulleted.d.ts +1 -0
  164. package/types/plugins/command/list_numbered.d.ts +1 -0
  165. package/types/plugins/dropdown/align.d.ts +1 -0
  166. package/types/plugins/dropdown/backgroundColor.d.ts +1 -0
  167. package/types/plugins/dropdown/font.d.ts +1 -0
  168. package/types/plugins/dropdown/fontColor.d.ts +1 -0
  169. package/types/plugins/dropdown/formatBlock.d.ts +3 -2
  170. package/types/plugins/dropdown/lineHeight.d.ts +1 -0
  171. package/types/plugins/dropdown/list.d.ts +1 -0
  172. package/types/plugins/dropdown/table.d.ts +6 -0
  173. package/types/plugins/input/fontSize.d.ts +1 -0
  174. package/types/plugins/modal/drawing.d.ts +4 -0
  175. package/types/plugins/modal/link.d.ts +32 -15
  176. package/types/suneditor.d.ts +13 -9
  177. package/types/typedef.d.ts +8 -0
@@ -1,565 +1,577 @@
1
- /**
2
- * @fileoverview Implements HueSlider.
3
- */
4
-
5
- import { dom, env } from '../helper';
6
- import Controller from './Controller';
7
-
8
- const { isMobile } = env;
9
-
10
- const SIZE = 240;
11
- const BAR_H = 28;
12
- const MIDDLE = SIZE / 2;
13
- const LIGHTNESS_CONT_VALUE = 50;
14
- const CLOSE_TO_CENTER_THRESHOLD = 3;
15
- const FIXED_DEC = 10;
16
- const SATURATION = 1;
17
- const GRADIENT_RADIUS = 14;
18
- const DEFAULT_COLOR_VALUE = { hex: '#FFFFFF', r: 255, g: 255, b: 255, h: 0, s: 1, l: 1 };
19
-
20
- let LIGHTNESS = 0;
21
- let isWheelragging = false;
22
- let isBarDragging = false;
23
- let wheelX = SIZE / 2;
24
- let wheelY = SIZE / 2;
25
- let finalColor = DEFAULT_COLOR_VALUE;
26
- let ctx;
27
-
28
- function CreateSliderCtx() {
29
- const offscreenCanvas = document.createElement('canvas');
30
- offscreenCanvas.width = SIZE;
31
- offscreenCanvas.height = SIZE;
32
-
33
- const html = /*html*/ `
34
- <div class="se-hue-slider-container" style="width: ${SIZE}px; height: ${SIZE}px;">
35
- <canvas class="se-hue-wheel" width="${SIZE}" height="${SIZE}"></canvas>
36
- <div class="se-hue-wheel-pointer"></div>
37
- </div>
38
- <div class="se-hue-gradient-container">
39
- <canvas class="se-hue-gradient" width="${SIZE}" height="${BAR_H}"></canvas>
40
- <div class="se-hue-gradient-pointer"></div>
41
- </div>
42
- <div class="se-hue-final-hex" style="width:${SIZE}px; height: ${BAR_H}px;">
43
- <div style="flex: 3; line-height: 1.5;">${DEFAULT_COLOR_VALUE.hex}</div>
44
- <div style="flex: 1; height: 100%; border: 1px solid #fff; outline: 1px solid #000;"></div>
45
- </div>
46
- `;
47
-
48
- const slider = dom.utils.createElement('DIV', { class: 'se-hue-slider' }, html);
49
- const wheelCanvas = /** @type {HTMLCanvasElement} */ (slider.querySelector('.se-hue-wheel'));
50
- const gradientBarCanvas = /** @type {HTMLCanvasElement} */ (slider.querySelector('.se-hue-gradient'));
51
- const currentColors = slider.querySelector('.se-hue-final-hex').children;
52
-
53
- return {
54
- slider,
55
- offscreenCanvas,
56
- offscreenCtx: offscreenCanvas.getContext('2d'),
57
- wheel: wheelCanvas,
58
- wheelCtx: wheelCanvas.getContext('2d'),
59
- wheelPointer: /** @type {HTMLElement} */ (slider.querySelector('.se-hue-wheel-pointer')),
60
- gradientBar: gradientBarCanvas,
61
- gradientPointer: /** @type {HTMLElement} */ (slider.querySelector('.se-hue-gradient-pointer')),
62
- fanalColorHex: /** @type {HTMLElement} */ (currentColors[0]),
63
- fanalColorBackground: /** @type {HTMLElement} */ (currentColors[1])
64
- };
65
- }
66
-
67
- /**
68
- * @typedef {import('../modules/Controller').ControllerParams} ControllerParams_hueSlider
69
- */
70
-
71
- /**
72
- * @typedef {Object} HueSliderColor
73
- * @property {string} hex - HEX color
74
- * @property {number} r - Red color value
75
- * @property {number} g - Green color value
76
- * @property {number} b - Blue color value
77
- * @property {number} h - Hue color value
78
- * @property {number} s - Saturation color value
79
- * @property {number} l - Lightness color value
80
- */
81
-
82
- /**
83
- * @typedef {Object} HueSliderParams
84
- * @property {Node} [form] The form element to attach the hue slider.
85
- * @property {boolean} [isNewForm] Whether to create a new form element.
86
- * @property {ControllerParams_hueSlider} [controllerOptions] Controller options
87
- */
88
-
89
- /**
90
- * @class
91
- * @description Create a Hue slider. (only create one at a time)
92
- * - When you call the .attach() method, the hue slider is appended to the form element.
93
- * It must be called every time it is used.
94
- */
95
- class HueSlider {
96
- /**
97
- * @constructor
98
- * @param {*} inst The instance object that called the constructor.
99
- * @param {HueSliderParams} [params={}] Hue slider options
100
- * @param {string} [className=""] The class name of the hue slider.
101
- */
102
- constructor(inst, params, className) {
103
- if (!params) params = {};
104
-
105
- this.editor = inst.editor;
106
- this.eventManager = inst.eventManager;
107
- this.inst = inst;
108
-
109
- // members
110
- this.form = params.form;
111
- this.ctx = {
112
- wheelX: wheelX,
113
- wheelY: wheelY,
114
- lightness: LIGHTNESS,
115
- wheelPointerX: '50%',
116
- wheelPointerY: '50%',
117
- gradientPointerX: 'calc(100% - 14px)',
118
- color: DEFAULT_COLOR_VALUE
119
- };
120
- this.isOpen = false;
121
- this.controlle = null;
122
- this.__globalMouseDown = null;
123
- this.__globalMouseMove = null;
124
- this.__globalMouseUp = null;
125
-
126
- // init default controller
127
- if (!params.isNewForm) {
128
- const hueController = CreateHTML_basicControllerForm(inst.editor, className);
129
- this.form = hueController.querySelector('.se-hue');
130
- this.controller = new Controller(this, hueController, { position: 'top', isWWTarget: false, ...params.controllerOptions });
131
-
132
- // buttons
133
- this.eventManager.addEvent(hueController.querySelector('.se-btn-success'), 'click', () => {
134
- inst.hueSliderAction(this.get());
135
- this.close();
136
- });
137
- this.eventManager.addEvent(hueController.querySelector('.se-btn-danger'), 'click', () => {
138
- this.close();
139
- });
140
- }
141
- }
142
-
143
- /**
144
- * @description Get the current color information.
145
- * @returns {HueSliderColor} color information
146
- */
147
- get() {
148
- return finalColor;
149
- }
150
-
151
- /**
152
- * @description Open the hue slider.
153
- * @param {Node} target The element to attach the hue slider.
154
- */
155
- open(target) {
156
- this.attach();
157
- this.controller.open(target, null, { isWWTarget: false, initMethod: null, addOffset: null });
158
- }
159
-
160
- /**
161
- * @description Reset information and close the hue slider.
162
- */
163
- off() {
164
- this.ctx = {
165
- gradientPointerX: gradientPointer.style.left,
166
- wheelPointerX: wheelPointer.style.left,
167
- wheelPointerY: wheelPointer.style.top,
168
- wheelX: wheelX,
169
- wheelY: wheelY,
170
- lightness: LIGHTNESS,
171
- color: ctx?.color || getWheelColor(wheelCtx)
172
- };
173
-
174
- this.controller.close();
175
- this.init();
176
- }
177
-
178
- /**
179
- * @description Close the hue slider. (include off method)
180
- * - Call the instance's hueSliderCancelAction method.
181
- */
182
- close() {
183
- this.off();
184
- this.inst.hueSliderCancelAction();
185
- }
186
-
187
- /**
188
- * @description Attach the hue slider to the form element.
189
- * @param {?Node=} form The element to attach the hue slider.
190
- */
191
- attach(form) {
192
- // drow
193
- this.init();
194
- (form || this.form).appendChild(slider);
195
- ctx = this.ctx;
196
- if (ctx) {
197
- wheelX = ctx.wheelX;
198
- wheelY = ctx.wheelY;
199
- LIGHTNESS = ctx.lightness;
200
- wheelPointer.style.left = ctx.wheelPointerX;
201
- wheelPointer.style.top = ctx.wheelPointerY;
202
- gradientPointer.style.left = ctx.gradientPointerX;
203
- setHex(ctx.color.hex);
204
-
205
- drawColorWheel();
206
- createGradientBar(getDefaultColor());
207
- }
208
-
209
- // event
210
- const downEvent = { name: 'mousedown', func: OnMousedown };
211
- const moveEvent = { name: 'mousemove', func: OnMousemove, option: true };
212
- const upEvent = {
213
- name: 'mouseup',
214
- func: () => {
215
- isWheelragging = false;
216
- isBarDragging = false;
217
- }
218
- };
219
-
220
- if (isMobile) {
221
- // mobile name
222
- downEvent.name = 'touchstart';
223
- moveEvent.name = 'touchmove';
224
- upEvent.name = 'touchend';
225
- // mobile func
226
- downEvent.func = OnTouchstart;
227
- moveEvent.func = OnTouchmove;
228
- }
229
-
230
- this.__globalMouseDown = this.eventManager.addGlobalEvent(downEvent.name, downEvent.func, { passive: false, capture: true });
231
- this.__globalMouseMove = this.eventManager.addGlobalEvent(moveEvent.name, moveEvent.func, true);
232
- this.__globalMouseUp = this.eventManager.addGlobalEvent(upEvent.name, upEvent.func, true);
233
- this.isOpen = true;
234
- }
235
-
236
- /**
237
- * @description Initialize the hue slider information.
238
- */
239
- init() {
240
- this.isOpen = false;
241
- isWheelragging = false;
242
- isBarDragging = false;
243
- if (this.__globalMouseDown) this.__globalMouseDown = this.eventManager.removeGlobalEvent(this.__globalMouseDown);
244
- if (this.__globalMouseMove) this.__globalMouseMove = this.eventManager.removeGlobalEvent(this.__globalMouseMove);
245
- if (this.__globalMouseUp) this.__globalMouseUp = this.eventManager.removeGlobalEvent(this.__globalMouseUp);
246
- }
247
- }
248
-
249
- // init
250
- const { slider, offscreenCanvas, offscreenCtx, wheel, wheelCtx, wheelPointer, gradientBar, gradientPointer, fanalColorHex, fanalColorBackground } = CreateSliderCtx();
251
-
252
- // mobile
253
- function OnTouchstart(event) {
254
- const { target, touches } = event;
255
- const clientX = touches[0].clientX;
256
- const clientY = touches[0].clientY;
257
-
258
- if (target === wheel) {
259
- event.preventDefault();
260
- isBarDragging = false;
261
- isWheelragging = true;
262
- updatePointer_wheel(clientX, clientY);
263
- } else if (target === gradientBar) {
264
- event.preventDefault();
265
- isBarDragging = true;
266
- isWheelragging = false;
267
- updatePointer_bar(clientX);
268
- }
269
- }
270
-
271
- function OnTouchmove(event) {
272
- event.preventDefault();
273
-
274
- const { touches } = event;
275
- const clientX = touches[0].clientX;
276
- const clientY = touches[0].clientY;
277
-
278
- if (isWheelragging) {
279
- updatePointer_wheel(clientX, clientY);
280
- } else if (isBarDragging) {
281
- updatePointer_bar(clientX);
282
- }
283
- }
284
-
285
- // pc
286
- function OnMousedown({ target, clientX, clientY }) {
287
- if (target === wheel) {
288
- isBarDragging = false;
289
- isWheelragging = true;
290
- updatePointer_wheel(clientX, clientY);
291
- } else if (target === gradientBar) {
292
- isBarDragging = true;
293
- isWheelragging = false;
294
- updatePointer_bar(clientX);
295
- }
296
- }
297
-
298
- function OnMousemove({ clientX, clientY }) {
299
- if (isWheelragging) {
300
- updatePointer_wheel(clientX, clientY);
301
- } else if (isBarDragging) {
302
- updatePointer_bar(clientX);
303
- }
304
- }
305
-
306
- function updatePointer_wheel(x, y) {
307
- const rect = wheel.getBoundingClientRect();
308
- x = x - rect.left - MIDDLE;
309
- y = y - rect.top - MIDDLE;
310
-
311
- const angle = (Math.atan2(y, x) * 180) / Math.PI;
312
- const distance = Math.min(Math.sqrt(x * x + y * y), MIDDLE);
313
-
314
- const posX = MIDDLE + distance * Math.cos((angle * Math.PI) / 180);
315
- const posY = MIDDLE + distance * Math.sin((angle * Math.PI) / 180);
316
-
317
- wheelPointer.style.left = `${posX}px`;
318
- wheelPointer.style.top = `${posY}px`;
319
-
320
- wheelPickedColor(posX, posY);
321
- setFinalColor();
322
- }
323
-
324
- function updatePointer_bar(x) {
325
- const rect = gradientBar.getBoundingClientRect();
326
- let posX = x - rect.left;
327
- posX = Math.max(GRADIENT_RADIUS, Math.min(posX, rect.width - GRADIENT_RADIUS));
328
-
329
- gradientPointer.style.left = `${posX}px`;
330
-
331
- selectGradientColor(x);
332
- setFinalColor();
333
- }
334
-
335
- function wheelPickedColor(posX, posY) {
336
- wheelX = posX;
337
- wheelY = posY;
338
- createGradientBar(getDefaultColor());
339
- }
340
-
341
- function createGradientBar(color) {
342
- const gradientBarCtx = gradientBar.getContext('2d');
343
- const gradient = gradientBarCtx.createLinearGradient(0, 0, gradientBar.width, 0);
344
-
345
- gradient.addColorStop(0, 'black'); // 왼쪽은 검은색
346
- gradient.addColorStop(1, color.hex); // 오른쪽은 선택한 색상
347
-
348
- gradientBarCtx.fillStyle = gradient;
349
- gradientBarCtx.fillRect(0, 0, gradientBar.width, gradientBar.height);
350
- }
351
-
352
- function getDefaultColor() {
353
- return getWheelColor(offscreenCtx);
354
- }
355
-
356
- function setFinalColor() {
357
- ctx.color = finalColor = getWheelColor(wheelCtx);
358
- setHex(finalColor.hex);
359
- }
360
-
361
- function setHex(hex) {
362
- fanalColorBackground.style.backgroundColor = fanalColorHex.textContent = hex;
363
- }
364
-
365
- function getWheelColor(wCtx) {
366
- const pixel = wCtx.getImageData(wheelX, wheelY, 1, 1).data;
367
- // eslint-disable-next-line prefer-const
368
- let [h, s, l] = rgbToHsl(pixel);
369
-
370
- // Calculate distance from the center of the wheel
371
- const dx = wheelX - MIDDLE;
372
- const dy = wheelY - MIDDLE;
373
- const distance = Math.sqrt(dx * dx + dy * dy);
374
-
375
- if (distance < CLOSE_TO_CENTER_THRESHOLD) {
376
- l = 1 - LIGHTNESS;
377
- }
378
-
379
- if (l > 1) l = 1;
380
- if (l < 0) l = 0;
381
-
382
- // Adjust lightness based on LIGHTNESS value
383
- const { r, g, b } = hslToRgb([h, s, l]);
384
-
385
- // Convert RGB to HEX
386
- const hex = `#${rgbToHex({ r, g, b })}`;
387
-
388
- return {
389
- hex,
390
- r,
391
- g,
392
- b,
393
- h,
394
- s,
395
- l: roundNumber(l)
396
- };
397
- }
398
-
399
- function selectGradientColor(x) {
400
- const boundingRect = gradientBar.getBoundingClientRect();
401
- let posX = x - boundingRect.left;
402
-
403
- if (posX < 0) posX = 0;
404
- if (posX > boundingRect.width) posX = boundingRect.width;
405
-
406
- const tolerance = GRADIENT_RADIUS;
407
-
408
- // If a click occurs near the end, the value is corrected all the way to the end.
409
- if (posX >= gradientBar.width - tolerance) {
410
- posX = gradientBar.width;
411
- } else if (posX <= tolerance) {
412
- posX = 0;
413
- }
414
-
415
- const normalizedLightness = 1 - posX / boundingRect.width; // 1 ~ 0
416
- LIGHTNESS = normalizedLightness; // 0 ~ 1
417
-
418
- drawColorWheel();
419
- }
420
-
421
- function drawColorWheel() {
422
- // init main canvas
423
- wheelCtx.clearRect(0, 0, SIZE, SIZE);
424
-
425
- // copy offscreen to main canvas
426
- wheelCtx.drawImage(offscreenCanvas, 0, 0);
427
-
428
- // drow dark wheel
429
- drawWheelGradient();
430
- }
431
-
432
- function drawWheelGradient() {
433
- wheelCtx.globalAlpha = LIGHTNESS; // 0: white, 1: black
434
- wheelCtx.fillStyle = 'black';
435
- wheelCtx.beginPath();
436
- wheelCtx.arc(MIDDLE, MIDDLE, MIDDLE, 0, 2 * Math.PI);
437
- wheelCtx.fill();
438
- wheelCtx.globalAlpha = 1.0;
439
- }
440
-
441
- function drawColorWheelToContext(context) {
442
- if (!context) throw new Error('Context not found.');
443
-
444
- const fixedSaturation = SATURATION * 100;
445
-
446
- for (let h = 0; h <= 360; h += 0.5) {
447
- for (let distance = 0; distance <= MIDDLE; distance += 1) {
448
- context.beginPath();
449
-
450
- const dynamicLightness = LIGHTNESS_CONT_VALUE + ((MIDDLE - distance) / MIDDLE) * 50;
451
-
452
- context.fillStyle = `hsl(${h}, ${fixedSaturation}%, ${dynamicLightness}%)`;
453
-
454
- const posX = MIDDLE + Math.cos(degreeToRadian(h)) * distance;
455
- const posY = MIDDLE - Math.sin(degreeToRadian(h)) * distance;
456
-
457
- context.arc(posX, posY, 1.5, 0, 2 * Math.PI);
458
- context.fill();
459
- }
460
- }
461
- }
462
-
463
- function degreeToRadian(deg) {
464
- return (deg * Math.PI) / 180;
465
- }
466
-
467
- function rgbToHsl([r, g, b]) {
468
- r /= 255;
469
- g /= 255;
470
- b /= 255;
471
-
472
- const max = Math.max(r, g, b),
473
- min = Math.min(r, g, b);
474
- let h, s;
475
- const l = (max + min) / 2;
476
-
477
- if (max === min) {
478
- h = s = 0; // achromatic
479
- } else {
480
- const d = max - min;
481
- s = l > 0.5 ? d / (2.0 - max - min) : d / (max + min);
482
-
483
- switch (max) {
484
- case r:
485
- h = (g - b) / d + (g < b ? 6 : 0);
486
- break;
487
- case g:
488
- h = (b - r) / d + 2;
489
- break;
490
- case b:
491
- h = (r - g) / d + 4;
492
- break;
493
- }
494
-
495
- h /= 6;
496
- }
497
-
498
- return [roundNumber(h), roundNumber(s), roundNumber(l)];
499
- }
500
-
501
- function hslToRgb([h, s, l]) {
502
- let r, g, b;
503
-
504
- if (s === 0) {
505
- r = g = b = l; // achromatic
506
- } else {
507
- const hue2rgb = function hue2rgb(p, q, t) {
508
- if (t < 0) t += 1;
509
- if (t > 1) t -= 1;
510
- if (t < 1 / 6) return p + (q - p) * 6 * t;
511
- if (t < 1 / 2) return q;
512
- if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
513
- return p;
514
- };
515
-
516
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
517
- const p = 2 * l - q;
518
- r = hue2rgb(p, q, h + 1 / 3);
519
- g = hue2rgb(p, q, h);
520
- b = hue2rgb(p, q, h - 1 / 3);
521
- }
522
-
523
- return {
524
- r: Math.round(r * 255),
525
- g: Math.round(g * 255),
526
- b: Math.round(b * 255)
527
- };
528
- }
529
-
530
- function rgbToHex({ r, g, b }) {
531
- let hexR = Math.floor(r).toString(16);
532
- if (r < 16) hexR = `0${hexR}`;
533
- let hexG = Math.floor(g).toString(16);
534
- if (g < 16) hexG = `0${hexG}`;
535
- let hexB = Math.floor(b).toString(16);
536
- if (b < 16) hexB = `0${hexB}`;
537
- return `${hexR}${hexG}${hexB}`.toUpperCase();
538
- }
539
-
540
- function roundNumber(num) {
541
- const factor = Math.pow(10, FIXED_DEC);
542
- return Math.round(num * factor) / factor;
543
- }
544
-
545
- // create
546
- drawColorWheelToContext(offscreenCtx);
547
- drawColorWheel();
548
-
549
- function CreateHTML_basicControllerForm({ lang, icons }, className) {
550
- const hueController = dom.utils.createElement(
551
- 'DIV',
552
- { class: `se-controller ${className}` },
553
- /*html*/ `
554
- <div class="se-hue"></div>
555
- <div class="se-form-group se-form-w0 se-form-flex-btn">
556
- <button type="button" class="se-btn se-btn-success" title="${lang.submitButton}" aria-label="${lang.submitButton}">${icons.checked}</button>
557
- <button type="button" class="se-btn se-btn-danger" title="${lang.close}" aria-label="${lang.close}">${icons.cancel}</button>
558
- </div>
559
- `
560
- );
561
-
562
- return hueController;
563
- }
564
-
565
- export default HueSlider;
1
+ /**
2
+ * @fileoverview Implements HueSlider.
3
+ */
4
+
5
+ import { dom, env } from '../helper';
6
+ import Controller from './Controller';
7
+
8
+ const { isTouchDevice } = env;
9
+
10
+ const SIZE = 240;
11
+ const BAR_H = 28;
12
+ const MIDDLE = SIZE / 2;
13
+ const LIGHTNESS_CONT_VALUE = 50;
14
+ const CLOSE_TO_CENTER_THRESHOLD = 3;
15
+ const FIXED_DEC = 10;
16
+ const SATURATION = 1;
17
+ const GRADIENT_RADIUS = 14;
18
+ const DEFAULT_COLOR_VALUE = { hex: '#FFFFFF', r: 255, g: 255, b: 255, h: 0, s: 1, l: 1 };
19
+
20
+ let LIGHTNESS = 0;
21
+ let isWheelragging = false;
22
+ let isBarDragging = false;
23
+ let wheelX = SIZE / 2;
24
+ let wheelY = SIZE / 2;
25
+ let finalColor = DEFAULT_COLOR_VALUE;
26
+ let ctx;
27
+
28
+ function CreateSliderCtx() {
29
+ const offscreenCanvas = document.createElement('canvas');
30
+ offscreenCanvas.width = SIZE;
31
+ offscreenCanvas.height = SIZE;
32
+
33
+ const html = /*html*/ `
34
+ <div class="se-hue-slider-container" style="width: ${SIZE}px; height: ${SIZE}px;">
35
+ <canvas class="se-hue-wheel" width="${SIZE}" height="${SIZE}"></canvas>
36
+ <div class="se-hue-wheel-pointer"></div>
37
+ </div>
38
+ <div class="se-hue-gradient-container">
39
+ <canvas class="se-hue-gradient" width="${SIZE}" height="${BAR_H}"></canvas>
40
+ <div class="se-hue-gradient-pointer"></div>
41
+ </div>
42
+ <div class="se-hue-final-hex" style="width:${SIZE}px; height: ${BAR_H}px;">
43
+ <div style="flex: 3; line-height: 1.5;">${DEFAULT_COLOR_VALUE.hex}</div>
44
+ <div style="flex: 1; height: 100%; border: 1px solid #fff; outline: 1px solid #000;"></div>
45
+ </div>
46
+ `;
47
+
48
+ const slider = dom.utils.createElement('DIV', { class: 'se-hue-slider' }, html);
49
+ const wheelCanvas = /** @type {HTMLCanvasElement} */ (slider.querySelector('.se-hue-wheel'));
50
+ const gradientBarCanvas = /** @type {HTMLCanvasElement} */ (slider.querySelector('.se-hue-gradient'));
51
+ const currentColors = slider.querySelector('.se-hue-final-hex').children;
52
+
53
+ return {
54
+ slider,
55
+ offscreenCanvas,
56
+ offscreenCtx: offscreenCanvas.getContext('2d'),
57
+ wheel: wheelCanvas,
58
+ wheelCtx: wheelCanvas.getContext('2d'),
59
+ wheelPointer: /** @type {HTMLElement} */ (slider.querySelector('.se-hue-wheel-pointer')),
60
+ gradientBar: gradientBarCanvas,
61
+ gradientPointer: /** @type {HTMLElement} */ (slider.querySelector('.se-hue-gradient-pointer')),
62
+ fanalColorHex: /** @type {HTMLElement} */ (currentColors[0]),
63
+ fanalColorBackground: /** @type {HTMLElement} */ (currentColors[1])
64
+ };
65
+ }
66
+
67
+ /**
68
+ * @typedef {import('../modules/Controller').ControllerParams} ControllerParams_hueSlider
69
+ */
70
+
71
+ /**
72
+ * @typedef {Object} HueSliderColor
73
+ * @property {string} hex - HEX color
74
+ * @property {number} r - Red color value
75
+ * @property {number} g - Green color value
76
+ * @property {number} b - Blue color value
77
+ * @property {number} h - Hue color value
78
+ * @property {number} s - Saturation color value
79
+ * @property {number} l - Lightness color value
80
+ */
81
+
82
+ /**
83
+ * @typedef {Object} HueSliderParams
84
+ * @property {Node} [form] The form element to attach the hue slider.
85
+ * @property {boolean} [isNewForm] Whether to create a new form element.
86
+ * @property {ControllerParams_hueSlider} [controllerOptions] Controller options
87
+ */
88
+
89
+ /**
90
+ * @class
91
+ * @description Create a Hue slider. (only create one at a time)
92
+ * - When you call the .attach() method, the hue slider is appended to the form element.
93
+ * It must be called every time it is used.
94
+ */
95
+ class HueSlider {
96
+ /**
97
+ * @constructor
98
+ * @param {*} inst The instance object that called the constructor.
99
+ * @param {HueSliderParams} [params={}] Hue slider options
100
+ * @param {string} [className=""] The class name of the hue slider.
101
+ */
102
+ constructor(inst, params, className) {
103
+ if (!params) params = {};
104
+
105
+ this.editor = inst.editor;
106
+ this.eventManager = inst.eventManager;
107
+ this.inst = inst;
108
+
109
+ // members
110
+ this.form = params.form;
111
+ this.ctx = {
112
+ wheelX: wheelX,
113
+ wheelY: wheelY,
114
+ lightness: LIGHTNESS,
115
+ wheelPointerX: '50%',
116
+ wheelPointerY: '50%',
117
+ gradientPointerX: 'calc(100% - 14px)',
118
+ color: DEFAULT_COLOR_VALUE
119
+ };
120
+ this.isOpen = false;
121
+ this.controlle = null;
122
+ this.__globalMouseDown = null;
123
+ this.__globalTouchMove = null;
124
+ this.__globalMouseUp = null;
125
+ this.__globalMouseMove = null;
126
+ this.__globalTouchStart = null;
127
+ this.__globalTouchEnd = null;
128
+
129
+ // init default controller
130
+ if (!params.isNewForm) {
131
+ const hueController = CreateHTML_basicControllerForm(inst.editor, className);
132
+ this.form = hueController.querySelector('.se-hue');
133
+ this.controller = new Controller(this, hueController, { position: 'top', isWWTarget: false, ...params.controllerOptions });
134
+
135
+ // buttons
136
+ this.eventManager.addEvent(hueController.querySelector('.se-btn-success'), 'click', () => {
137
+ inst.hueSliderAction(this.get());
138
+ this.close();
139
+ });
140
+ this.eventManager.addEvent(hueController.querySelector('.se-btn-danger'), 'click', () => {
141
+ this.close();
142
+ });
143
+ }
144
+ }
145
+
146
+ /**
147
+ * @description Get the current color information.
148
+ * @returns {HueSliderColor} color information
149
+ */
150
+ get() {
151
+ return finalColor;
152
+ }
153
+
154
+ /**
155
+ * @description Open the hue slider.
156
+ * @param {Node} target The element to attach the hue slider.
157
+ */
158
+ open(target) {
159
+ this.attach();
160
+ this.controller.open(target, null, { isWWTarget: false, initMethod: null, addOffset: null });
161
+ }
162
+
163
+ /**
164
+ * @description Reset information and close the hue slider.
165
+ */
166
+ off() {
167
+ this.ctx = {
168
+ gradientPointerX: gradientPointer.style.left,
169
+ wheelPointerX: wheelPointer.style.left,
170
+ wheelPointerY: wheelPointer.style.top,
171
+ wheelX: wheelX,
172
+ wheelY: wheelY,
173
+ lightness: LIGHTNESS,
174
+ color: ctx?.color || getWheelColor(wheelCtx)
175
+ };
176
+
177
+ this.controller.close();
178
+ this.init();
179
+ }
180
+
181
+ /**
182
+ * @description Close the hue slider. (include off method)
183
+ * - Call the instance's hueSliderCancelAction method.
184
+ */
185
+ close() {
186
+ this.off();
187
+ this.inst.hueSliderCancelAction();
188
+ }
189
+
190
+ /**
191
+ * @description Attach the hue slider to the form element.
192
+ * @param {?Node=} form The element to attach the hue slider.
193
+ */
194
+ attach(form) {
195
+ // drow
196
+ this.init();
197
+ (form || this.form).appendChild(slider);
198
+ ctx = this.ctx;
199
+ if (ctx) {
200
+ wheelX = ctx.wheelX;
201
+ wheelY = ctx.wheelY;
202
+ LIGHTNESS = ctx.lightness;
203
+ wheelPointer.style.left = ctx.wheelPointerX;
204
+ wheelPointer.style.top = ctx.wheelPointerY;
205
+ gradientPointer.style.left = ctx.gradientPointerX;
206
+ setHex(ctx.color.hex);
207
+
208
+ drawColorWheel();
209
+ createGradientBar(getDefaultColor());
210
+ }
211
+
212
+ // touch event
213
+ if (isTouchDevice) {
214
+ // mobile name
215
+ this.__globalTouchStart = this.eventManager.addGlobalEvent('touchstart', OnTouchstart, { passive: false, capture: true });
216
+ this.__globalTouchMove = this.eventManager.addGlobalEvent('touchmove', OnTouchmove, true);
217
+ this.__globalTouchEnd = this.eventManager.addGlobalEvent(
218
+ 'touchend',
219
+ () => {
220
+ isWheelragging = false;
221
+ isBarDragging = false;
222
+ },
223
+ true
224
+ );
225
+ }
226
+
227
+ // mouse event
228
+ this.__globalMouseDown = this.eventManager.addGlobalEvent('mousedown', OnMousedown, { passive: false, capture: true });
229
+ this.__globalMouseMove = this.eventManager.addGlobalEvent('mousemove', OnMousemove, true);
230
+ this.__globalMouseUp = this.eventManager.addGlobalEvent(
231
+ 'mouseup',
232
+ () => {
233
+ isWheelragging = false;
234
+ isBarDragging = false;
235
+ },
236
+ true
237
+ );
238
+
239
+ // open
240
+ this.isOpen = true;
241
+ }
242
+
243
+ /**
244
+ * @description Initialize the hue slider information.
245
+ */
246
+ init() {
247
+ this.isOpen = false;
248
+ isWheelragging = false;
249
+ isBarDragging = false;
250
+
251
+ if (this.__globalMouseDown) this.__globalMouseDown = this.eventManager.removeGlobalEvent(this.__globalMouseDown);
252
+ if (this.__globalMouseMove) this.__globalMouseMove = this.eventManager.removeGlobalEvent(this.__globalMouseMove);
253
+ if (this.__globalMouseUp) this.__globalMouseUp = this.eventManager.removeGlobalEvent(this.__globalMouseUp);
254
+
255
+ if (this.__globalTouchStart) this.__globalTouchStart = this.eventManager.removeGlobalEvent(this.__globalTouchStart);
256
+ if (this.__globalTouchMove) this.__globalTouchMove = this.eventManager.removeGlobalEvent(this.__globalTouchMove);
257
+ if (this.__globalTouchEnd) this.__globalTouchEnd = this.eventManager.removeGlobalEvent(this.__globalTouchEnd);
258
+ }
259
+ }
260
+
261
+ // init
262
+ const { slider, offscreenCanvas, offscreenCtx, wheel, wheelCtx, wheelPointer, gradientBar, gradientPointer, fanalColorHex, fanalColorBackground } = CreateSliderCtx();
263
+
264
+ // mobile
265
+ function OnTouchstart(event) {
266
+ const { target, touches } = event;
267
+ const clientX = touches[0].clientX;
268
+ const clientY = touches[0].clientY;
269
+
270
+ if (target === wheel) {
271
+ event.preventDefault();
272
+ isBarDragging = false;
273
+ isWheelragging = true;
274
+ updatePointer_wheel(clientX, clientY);
275
+ } else if (target === gradientBar) {
276
+ event.preventDefault();
277
+ isBarDragging = true;
278
+ isWheelragging = false;
279
+ updatePointer_bar(clientX);
280
+ }
281
+ }
282
+
283
+ function OnTouchmove(event) {
284
+ event.preventDefault();
285
+
286
+ const { touches } = event;
287
+ const clientX = touches[0].clientX;
288
+ const clientY = touches[0].clientY;
289
+
290
+ if (isWheelragging) {
291
+ updatePointer_wheel(clientX, clientY);
292
+ } else if (isBarDragging) {
293
+ updatePointer_bar(clientX);
294
+ }
295
+ }
296
+
297
+ // pc
298
+ function OnMousedown({ target, clientX, clientY }) {
299
+ if (target === wheel) {
300
+ isBarDragging = false;
301
+ isWheelragging = true;
302
+ updatePointer_wheel(clientX, clientY);
303
+ } else if (target === gradientBar) {
304
+ isBarDragging = true;
305
+ isWheelragging = false;
306
+ updatePointer_bar(clientX);
307
+ }
308
+ }
309
+
310
+ function OnMousemove({ clientX, clientY }) {
311
+ if (isWheelragging) {
312
+ updatePointer_wheel(clientX, clientY);
313
+ } else if (isBarDragging) {
314
+ updatePointer_bar(clientX);
315
+ }
316
+ }
317
+
318
+ function updatePointer_wheel(x, y) {
319
+ const rect = wheel.getBoundingClientRect();
320
+ x = x - rect.left - MIDDLE;
321
+ y = y - rect.top - MIDDLE;
322
+
323
+ const angle = (Math.atan2(y, x) * 180) / Math.PI;
324
+ const distance = Math.min(Math.sqrt(x * x + y * y), MIDDLE);
325
+
326
+ const posX = MIDDLE + distance * Math.cos((angle * Math.PI) / 180);
327
+ const posY = MIDDLE + distance * Math.sin((angle * Math.PI) / 180);
328
+
329
+ wheelPointer.style.left = `${posX}px`;
330
+ wheelPointer.style.top = `${posY}px`;
331
+
332
+ wheelPickedColor(posX, posY);
333
+ setFinalColor();
334
+ }
335
+
336
+ function updatePointer_bar(x) {
337
+ const rect = gradientBar.getBoundingClientRect();
338
+ let posX = x - rect.left;
339
+ posX = Math.max(GRADIENT_RADIUS, Math.min(posX, rect.width - GRADIENT_RADIUS));
340
+
341
+ gradientPointer.style.left = `${posX}px`;
342
+
343
+ selectGradientColor(x);
344
+ setFinalColor();
345
+ }
346
+
347
+ function wheelPickedColor(posX, posY) {
348
+ wheelX = posX;
349
+ wheelY = posY;
350
+ createGradientBar(getDefaultColor());
351
+ }
352
+
353
+ function createGradientBar(color) {
354
+ const gradientBarCtx = gradientBar.getContext('2d');
355
+ const gradient = gradientBarCtx.createLinearGradient(0, 0, gradientBar.width, 0);
356
+
357
+ gradient.addColorStop(0, 'black'); // 왼쪽은 검은색
358
+ gradient.addColorStop(1, color.hex); // 오른쪽은 선택한 색상
359
+
360
+ gradientBarCtx.fillStyle = gradient;
361
+ gradientBarCtx.fillRect(0, 0, gradientBar.width, gradientBar.height);
362
+ }
363
+
364
+ function getDefaultColor() {
365
+ return getWheelColor(offscreenCtx);
366
+ }
367
+
368
+ function setFinalColor() {
369
+ ctx.color = finalColor = getWheelColor(wheelCtx);
370
+ setHex(finalColor.hex);
371
+ }
372
+
373
+ function setHex(hex) {
374
+ fanalColorBackground.style.backgroundColor = fanalColorHex.textContent = hex;
375
+ }
376
+
377
+ function getWheelColor(wCtx) {
378
+ const pixel = wCtx.getImageData(wheelX, wheelY, 1, 1).data;
379
+ // eslint-disable-next-line prefer-const
380
+ let [h, s, l] = rgbToHsl(pixel);
381
+
382
+ // Calculate distance from the center of the wheel
383
+ const dx = wheelX - MIDDLE;
384
+ const dy = wheelY - MIDDLE;
385
+ const distance = Math.sqrt(dx * dx + dy * dy);
386
+
387
+ if (distance < CLOSE_TO_CENTER_THRESHOLD) {
388
+ l = 1 - LIGHTNESS;
389
+ }
390
+
391
+ if (l > 1) l = 1;
392
+ if (l < 0) l = 0;
393
+
394
+ // Adjust lightness based on LIGHTNESS value
395
+ const { r, g, b } = hslToRgb([h, s, l]);
396
+
397
+ // Convert RGB to HEX
398
+ const hex = `#${rgbToHex({ r, g, b })}`;
399
+
400
+ return {
401
+ hex,
402
+ r,
403
+ g,
404
+ b,
405
+ h,
406
+ s,
407
+ l: roundNumber(l)
408
+ };
409
+ }
410
+
411
+ function selectGradientColor(x) {
412
+ const boundingRect = gradientBar.getBoundingClientRect();
413
+ let posX = x - boundingRect.left;
414
+
415
+ if (posX < 0) posX = 0;
416
+ if (posX > boundingRect.width) posX = boundingRect.width;
417
+
418
+ const tolerance = GRADIENT_RADIUS;
419
+
420
+ // If a click occurs near the end, the value is corrected all the way to the end.
421
+ if (posX >= gradientBar.width - tolerance) {
422
+ posX = gradientBar.width;
423
+ } else if (posX <= tolerance) {
424
+ posX = 0;
425
+ }
426
+
427
+ const normalizedLightness = 1 - posX / boundingRect.width; // 1 ~ 0
428
+ LIGHTNESS = normalizedLightness; // 0 ~ 1
429
+
430
+ drawColorWheel();
431
+ }
432
+
433
+ function drawColorWheel() {
434
+ // init main canvas
435
+ wheelCtx.clearRect(0, 0, SIZE, SIZE);
436
+
437
+ // copy offscreen to main canvas
438
+ wheelCtx.drawImage(offscreenCanvas, 0, 0);
439
+
440
+ // drow dark wheel
441
+ drawWheelGradient();
442
+ }
443
+
444
+ function drawWheelGradient() {
445
+ wheelCtx.globalAlpha = LIGHTNESS; // 0: white, 1: black
446
+ wheelCtx.fillStyle = 'black';
447
+ wheelCtx.beginPath();
448
+ wheelCtx.arc(MIDDLE, MIDDLE, MIDDLE, 0, 2 * Math.PI);
449
+ wheelCtx.fill();
450
+ wheelCtx.globalAlpha = 1.0;
451
+ }
452
+
453
+ function drawColorWheelToContext(context) {
454
+ if (!context) throw new Error('Context not found.');
455
+
456
+ const fixedSaturation = SATURATION * 100;
457
+
458
+ for (let h = 0; h <= 360; h += 0.5) {
459
+ for (let distance = 0; distance <= MIDDLE; distance += 1) {
460
+ context.beginPath();
461
+
462
+ const dynamicLightness = LIGHTNESS_CONT_VALUE + ((MIDDLE - distance) / MIDDLE) * 50;
463
+
464
+ context.fillStyle = `hsl(${h}, ${fixedSaturation}%, ${dynamicLightness}%)`;
465
+
466
+ const posX = MIDDLE + Math.cos(degreeToRadian(h)) * distance;
467
+ const posY = MIDDLE - Math.sin(degreeToRadian(h)) * distance;
468
+
469
+ context.arc(posX, posY, 1.5, 0, 2 * Math.PI);
470
+ context.fill();
471
+ }
472
+ }
473
+ }
474
+
475
+ function degreeToRadian(deg) {
476
+ return (deg * Math.PI) / 180;
477
+ }
478
+
479
+ function rgbToHsl([r, g, b]) {
480
+ r /= 255;
481
+ g /= 255;
482
+ b /= 255;
483
+
484
+ const max = Math.max(r, g, b),
485
+ min = Math.min(r, g, b);
486
+ let h, s;
487
+ const l = (max + min) / 2;
488
+
489
+ if (max === min) {
490
+ h = s = 0; // achromatic
491
+ } else {
492
+ const d = max - min;
493
+ s = l > 0.5 ? d / (2.0 - max - min) : d / (max + min);
494
+
495
+ switch (max) {
496
+ case r:
497
+ h = (g - b) / d + (g < b ? 6 : 0);
498
+ break;
499
+ case g:
500
+ h = (b - r) / d + 2;
501
+ break;
502
+ case b:
503
+ h = (r - g) / d + 4;
504
+ break;
505
+ }
506
+
507
+ h /= 6;
508
+ }
509
+
510
+ return [roundNumber(h), roundNumber(s), roundNumber(l)];
511
+ }
512
+
513
+ function hslToRgb([h, s, l]) {
514
+ let r, g, b;
515
+
516
+ if (s === 0) {
517
+ r = g = b = l; // achromatic
518
+ } else {
519
+ const hue2rgb = function hue2rgb(p, q, t) {
520
+ if (t < 0) t += 1;
521
+ if (t > 1) t -= 1;
522
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
523
+ if (t < 1 / 2) return q;
524
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
525
+ return p;
526
+ };
527
+
528
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
529
+ const p = 2 * l - q;
530
+ r = hue2rgb(p, q, h + 1 / 3);
531
+ g = hue2rgb(p, q, h);
532
+ b = hue2rgb(p, q, h - 1 / 3);
533
+ }
534
+
535
+ return {
536
+ r: Math.round(r * 255),
537
+ g: Math.round(g * 255),
538
+ b: Math.round(b * 255)
539
+ };
540
+ }
541
+
542
+ function rgbToHex({ r, g, b }) {
543
+ let hexR = Math.floor(r).toString(16);
544
+ if (r < 16) hexR = `0${hexR}`;
545
+ let hexG = Math.floor(g).toString(16);
546
+ if (g < 16) hexG = `0${hexG}`;
547
+ let hexB = Math.floor(b).toString(16);
548
+ if (b < 16) hexB = `0${hexB}`;
549
+ return `${hexR}${hexG}${hexB}`.toUpperCase();
550
+ }
551
+
552
+ function roundNumber(num) {
553
+ const factor = Math.pow(10, FIXED_DEC);
554
+ return Math.round(num * factor) / factor;
555
+ }
556
+
557
+ // create
558
+ drawColorWheelToContext(offscreenCtx);
559
+ drawColorWheel();
560
+
561
+ function CreateHTML_basicControllerForm({ lang, icons }, className) {
562
+ const hueController = dom.utils.createElement(
563
+ 'DIV',
564
+ { class: `se-controller ${className}` },
565
+ /*html*/ `
566
+ <div class="se-hue"></div>
567
+ <div class="se-form-group se-form-w0 se-form-flex-btn">
568
+ <button type="button" class="se-btn se-btn-success" title="${lang.submitButton}" aria-label="${lang.submitButton}">${icons.checked}</button>
569
+ <button type="button" class="se-btn se-btn-danger" title="${lang.close}" aria-label="${lang.close}">${icons.cancel}</button>
570
+ </div>
571
+ `
572
+ );
573
+
574
+ return hueController;
575
+ }
576
+
577
+ export default HueSlider;