@thednp/color-picker 0.0.1-alpha1 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +63 -26
- package/dist/css/color-picker.css +504 -337
- package/dist/css/color-picker.min.css +2 -0
- package/dist/css/color-picker.rtl.css +529 -0
- package/dist/css/color-picker.rtl.min.css +2 -0
- package/dist/js/color-picker-element-esm.js +3851 -2
- package/dist/js/color-picker-element-esm.min.js +2 -0
- package/dist/js/color-picker-element.js +2086 -1278
- package/dist/js/color-picker-element.min.js +2 -2
- package/dist/js/color-picker-esm.js +3742 -0
- package/dist/js/color-picker-esm.min.js +2 -0
- package/dist/js/color-picker.js +2030 -1286
- package/dist/js/color-picker.min.js +2 -2
- package/package.json +18 -9
- package/src/js/color-palette.js +71 -0
- package/src/js/color-picker-element.js +62 -16
- package/src/js/color-picker.js +734 -618
- package/src/js/color.js +621 -358
- package/src/js/index.js +0 -9
- package/src/js/util/colorNames.js +2 -152
- package/src/js/util/colorPickerLabels.js +22 -0
- package/src/js/util/getColorControls.js +103 -0
- package/src/js/util/getColorForm.js +26 -19
- package/src/js/util/getColorMenu.js +88 -0
- package/src/js/util/isValidJSON.js +13 -0
- package/src/js/util/nonColors.js +5 -0
- package/src/js/util/roundPart.js +9 -0
- package/src/js/util/setCSSProperties.js +12 -0
- package/src/js/util/tabindex.js +3 -0
- package/src/js/util/templates.js +1 -0
- package/src/scss/color-picker.rtl.scss +23 -0
- package/src/scss/color-picker.scss +449 -0
- package/types/cp.d.ts +263 -162
- package/types/index.d.ts +9 -2
- package/types/source/source.ts +2 -1
- package/types/source/types.d.ts +28 -5
- package/dist/js/color-picker.esm.js +0 -2998
- package/dist/js/color-picker.esm.min.js +0 -2
- package/src/js/util/getColorControl.js +0 -49
- package/src/js/util/init.js +0 -14
package/src/js/color-picker.js
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
import { addListener, removeListener } from 'event-listener.js';
|
2
2
|
|
3
|
+
import ariaDescription from 'shorter-js/src/strings/ariaDescription';
|
4
|
+
// import ariaLabel from 'shorter-js/src/strings/ariaLabel';
|
3
5
|
import ariaSelected from 'shorter-js/src/strings/ariaSelected';
|
4
6
|
import ariaExpanded from 'shorter-js/src/strings/ariaExpanded';
|
7
|
+
import ariaValueText from 'shorter-js/src/strings/ariaValueText';
|
8
|
+
import ariaValueNow from 'shorter-js/src/strings/ariaValueNow';
|
9
|
+
import ariaHasPopup from 'shorter-js/src/strings/ariaHasPopup';
|
5
10
|
import ariaHidden from 'shorter-js/src/strings/ariaHidden';
|
6
11
|
import ariaLabelledBy from 'shorter-js/src/strings/ariaLabelledBy';
|
7
12
|
import keyArrowDown from 'shorter-js/src/strings/keyArrowDown';
|
@@ -11,62 +16,82 @@ import keyArrowRight from 'shorter-js/src/strings/keyArrowRight';
|
|
11
16
|
import keyEnter from 'shorter-js/src/strings/keyEnter';
|
12
17
|
import keySpace from 'shorter-js/src/strings/keySpace';
|
13
18
|
import keyEscape from 'shorter-js/src/strings/keyEscape';
|
19
|
+
import focusinEvent from 'shorter-js/src/strings/focusinEvent';
|
20
|
+
import mouseclickEvent from 'shorter-js/src/strings/mouseclickEvent';
|
21
|
+
import keydownEvent from 'shorter-js/src/strings/keydownEvent';
|
22
|
+
import changeEvent from 'shorter-js/src/strings/changeEvent';
|
23
|
+
import touchstartEvent from 'shorter-js/src/strings/touchstartEvent';
|
24
|
+
import touchmoveEvent from 'shorter-js/src/strings/touchmoveEvent';
|
25
|
+
import touchendEvent from 'shorter-js/src/strings/touchendEvent';
|
26
|
+
import mousedownEvent from 'shorter-js/src/strings/mousedownEvent';
|
27
|
+
import mousemoveEvent from 'shorter-js/src/strings/mousemoveEvent';
|
28
|
+
import mouseupEvent from 'shorter-js/src/strings/mouseupEvent';
|
29
|
+
import scrollEvent from 'shorter-js/src/strings/scrollEvent';
|
30
|
+
import keyupEvent from 'shorter-js/src/strings/keyupEvent';
|
31
|
+
import resizeEvent from 'shorter-js/src/strings/resizeEvent';
|
32
|
+
import focusoutEvent from 'shorter-js/src/strings/focusoutEvent';
|
14
33
|
|
15
34
|
import isMobile from 'shorter-js/src/boolean/isMobile';
|
35
|
+
import getDocument from 'shorter-js/src/get/getDocument';
|
36
|
+
import getDocumentElement from 'shorter-js/src/get/getDocumentElement';
|
37
|
+
import getWindow from 'shorter-js/src/get/getWindow';
|
38
|
+
import getElementStyle from 'shorter-js/src/get/getElementStyle';
|
16
39
|
import getUID from 'shorter-js/src/get/getUID';
|
17
40
|
import getBoundingClientRect from 'shorter-js/src/get/getBoundingClientRect';
|
41
|
+
import getElementTransitionDuration from 'shorter-js/src/get/getElementTransitionDuration';
|
18
42
|
import querySelector from 'shorter-js/src/selectors/querySelector';
|
19
|
-
import querySelectorAll from 'shorter-js/src/selectors/querySelectorAll';
|
20
43
|
import closest from 'shorter-js/src/selectors/closest';
|
44
|
+
import getElementsByClassName from 'shorter-js/src/selectors/getElementsByClassName';
|
21
45
|
import createElement from 'shorter-js/src/misc/createElement';
|
22
46
|
import createElementNS from 'shorter-js/src/misc/createElementNS';
|
23
47
|
import dispatchEvent from 'shorter-js/src/misc/dispatchEvent';
|
24
48
|
import ObjectAssign from 'shorter-js/src/misc/ObjectAssign';
|
25
49
|
import Data, { getInstance } from 'shorter-js/src/misc/data';
|
50
|
+
import setElementStyle from 'shorter-js/src/misc/setElementStyle';
|
51
|
+
import normalizeOptions from 'shorter-js/src/misc/normalizeOptions';
|
52
|
+
import reflow from 'shorter-js/src/misc/reflow';
|
53
|
+
import focus from 'shorter-js/src/misc/focus';
|
26
54
|
import hasClass from 'shorter-js/src/class/hasClass';
|
27
55
|
import addClass from 'shorter-js/src/class/addClass';
|
28
56
|
import removeClass from 'shorter-js/src/class/removeClass';
|
29
|
-
import hasAttribute from 'shorter-js/src/attr/hasAttribute';
|
30
57
|
import setAttribute from 'shorter-js/src/attr/setAttribute';
|
31
58
|
import getAttribute from 'shorter-js/src/attr/getAttribute';
|
32
59
|
import removeAttribute from 'shorter-js/src/attr/removeAttribute';
|
33
60
|
|
61
|
+
// ColorPicker Util
|
62
|
+
// ================
|
63
|
+
import colorPickerLabels from './util/colorPickerLabels';
|
64
|
+
import colorNames from './util/colorNames';
|
65
|
+
import nonColors from './util/nonColors';
|
34
66
|
import getColorForm from './util/getColorForm';
|
35
|
-
import
|
67
|
+
import getColorControls from './util/getColorControls';
|
68
|
+
import getColorMenu from './util/getColorMenu';
|
36
69
|
import vHidden from './util/vHidden';
|
70
|
+
import tabIndex from './util/tabindex';
|
71
|
+
import isValidJSON from './util/isValidJSON';
|
72
|
+
import roundPart from './util/roundPart';
|
37
73
|
import Color from './color';
|
74
|
+
import ColorPalette from './color-palette';
|
75
|
+
import Version from './version';
|
38
76
|
|
39
77
|
// ColorPicker GC
|
40
78
|
// ==============
|
41
79
|
const colorPickerString = 'color-picker';
|
42
80
|
const colorPickerSelector = `[data-function="${colorPickerString}"]`;
|
43
|
-
const
|
44
|
-
const
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
formatLabel: 'Colour Format',
|
51
|
-
formatHEX: 'Hexadecimal Format',
|
52
|
-
formatRGB: 'RGB Format',
|
53
|
-
formatHSL: 'HSL Format',
|
54
|
-
alphaLabel: 'Alpha',
|
55
|
-
appearanceLabel: 'Colour Appearance',
|
56
|
-
hexLabel: 'Hexadecimal',
|
57
|
-
hueLabel: 'Hue',
|
58
|
-
saturationLabel: 'Saturation',
|
59
|
-
lightnessLabel: 'Lightness',
|
60
|
-
redLabel: 'Red',
|
61
|
-
greenLabel: 'Green',
|
62
|
-
blueLabel: 'Blue',
|
81
|
+
const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
|
82
|
+
const colorPickerDefaults = {
|
83
|
+
componentLabels: colorPickerLabels,
|
84
|
+
colorLabels: colorNames,
|
85
|
+
format: 'rgb',
|
86
|
+
colorPresets: false,
|
87
|
+
colorKeywords: false,
|
63
88
|
};
|
64
89
|
|
65
90
|
// ColorPicker Static Methods
|
66
91
|
// ==========================
|
67
92
|
|
68
93
|
/** @type {CP.GetInstance<ColorPicker>} */
|
69
|
-
const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
|
94
|
+
export const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
|
70
95
|
|
71
96
|
/** @type {CP.InitCallback<ColorPicker>} */
|
72
97
|
const initColorPicker = (element) => new ColorPicker(element);
|
@@ -74,165 +99,94 @@ const initColorPicker = (element) => new ColorPicker(element);
|
|
74
99
|
// ColorPicker Private Methods
|
75
100
|
// ===========================
|
76
101
|
|
77
|
-
/**
|
78
|
-
* Add / remove `ColorPicker` main event listeners.
|
79
|
-
* @param {ColorPicker} self
|
80
|
-
* @param {boolean=} action
|
81
|
-
*/
|
82
|
-
function toggleEvents(self, action) {
|
83
|
-
const fn = action ? addListener : removeListener;
|
84
|
-
const { input, pickerToggle, menuToggle } = self;
|
85
|
-
|
86
|
-
fn(input, 'focusin', self.showPicker);
|
87
|
-
fn(pickerToggle, 'click', self.togglePicker);
|
88
|
-
|
89
|
-
fn(input, 'keydown', self.keyHandler);
|
90
|
-
|
91
|
-
if (menuToggle) {
|
92
|
-
fn(menuToggle, 'click', self.toggleMenu);
|
93
|
-
}
|
94
|
-
}
|
95
|
-
|
96
102
|
/**
|
97
103
|
* Generate HTML markup and update instance properties.
|
98
104
|
* @param {ColorPicker} self
|
99
105
|
*/
|
100
106
|
function initCallback(self) {
|
101
107
|
const {
|
102
|
-
input, parent, format, id, componentLabels,
|
108
|
+
input, parent, format, id, componentLabels, colorKeywords, colorPresets,
|
103
109
|
} = self;
|
104
110
|
const colorValue = getAttribute(input, 'value') || '#fff';
|
105
111
|
|
106
112
|
const {
|
107
|
-
toggleLabel,
|
113
|
+
toggleLabel, pickerLabel, formatLabel, hexLabel,
|
108
114
|
} = componentLabels;
|
109
115
|
|
110
116
|
// update color
|
111
117
|
const color = nonColors.includes(colorValue) ? '#fff' : colorValue;
|
112
|
-
self.color = new Color(color,
|
118
|
+
self.color = new Color(color, format);
|
113
119
|
|
114
120
|
// set initial controls dimensions
|
115
121
|
// make the controls smaller on mobile
|
116
|
-
const cv1w = isMobile ? 150 : 230;
|
117
|
-
const cvh = isMobile ? 150 : 230;
|
118
|
-
const cv2w = 21;
|
119
122
|
const dropClass = isMobile ? ' mobile' : '';
|
120
|
-
const
|
121
|
-
const ctrl2Labelledby = format === 'hsl' ? `appearance2_${id}` : `appearance_${id} appearance2_${id}`;
|
123
|
+
const formatString = format === 'hex' ? hexLabel : format.toUpperCase();
|
122
124
|
|
123
125
|
const pickerBtn = createElement({
|
126
|
+
id: `picker-btn-${id}`,
|
124
127
|
tagName: 'button',
|
125
|
-
className: 'picker-toggle
|
126
|
-
ariaExpanded: 'false',
|
127
|
-
ariaHasPopup: 'true',
|
128
|
-
ariaLive: 'polite',
|
128
|
+
className: 'picker-toggle btn-appearance',
|
129
129
|
});
|
130
|
-
setAttribute(pickerBtn,
|
130
|
+
setAttribute(pickerBtn, ariaExpanded, 'false');
|
131
|
+
setAttribute(pickerBtn, ariaHasPopup, 'true');
|
131
132
|
pickerBtn.append(createElement({
|
132
133
|
tagName: 'span',
|
133
134
|
className: vHidden,
|
134
|
-
innerText:
|
135
|
+
innerText: `${pickerLabel}. ${formatLabel}: ${formatString}`,
|
135
136
|
}));
|
136
137
|
|
137
|
-
const
|
138
|
+
const pickerDropdown = createElement({
|
138
139
|
tagName: 'div',
|
139
140
|
className: `color-dropdown picker${dropClass}`,
|
140
141
|
});
|
141
|
-
setAttribute(
|
142
|
-
setAttribute(
|
143
|
-
colorPickerDropdown.append(
|
144
|
-
createElement({
|
145
|
-
tagName: 'label',
|
146
|
-
className: vHidden,
|
147
|
-
ariaHidden: 'true',
|
148
|
-
id: `picker-label-${id}`,
|
149
|
-
innerText: `${pickerLabel}`,
|
150
|
-
}),
|
151
|
-
createElement({
|
152
|
-
tagName: 'label',
|
153
|
-
className: vHidden,
|
154
|
-
ariaHidden: 'true',
|
155
|
-
id: `format-label-${id}`,
|
156
|
-
innerText: `${formatLabel}`,
|
157
|
-
}),
|
158
|
-
createElement({
|
159
|
-
tagName: 'label',
|
160
|
-
className: `color-appearance ${vHidden}`,
|
161
|
-
ariaHidden: 'true',
|
162
|
-
ariaLive: 'polite',
|
163
|
-
id: `appearance_${id}`,
|
164
|
-
innerText: `${appearanceLabel}`,
|
165
|
-
}),
|
166
|
-
);
|
167
|
-
|
168
|
-
const colorControls = createElement({
|
169
|
-
tagName: 'div',
|
170
|
-
className: `color-controls ${format}`,
|
171
|
-
});
|
172
|
-
|
173
|
-
colorControls.append(
|
174
|
-
getColorControl(1, id, cv1w, cvh, ctrl1Labelledby),
|
175
|
-
getColorControl(2, id, cv2w, cvh, ctrl2Labelledby),
|
176
|
-
);
|
177
|
-
|
178
|
-
if (format !== 'hex') {
|
179
|
-
colorControls.append(
|
180
|
-
getColorControl(3, id, cv2w, cvh),
|
181
|
-
);
|
182
|
-
}
|
142
|
+
setAttribute(pickerDropdown, ariaLabelledBy, `picker-btn-${id}`);
|
143
|
+
setAttribute(pickerDropdown, 'role', 'group');
|
183
144
|
|
184
|
-
|
145
|
+
const colorControls = getColorControls(self);
|
185
146
|
const colorForm = getColorForm(self);
|
186
|
-
colorPickerDropdown.append(colorControls, colorForm);
|
187
|
-
parent.append(pickerBtn, colorPickerDropdown);
|
188
147
|
|
189
|
-
|
190
|
-
|
191
|
-
|
148
|
+
pickerDropdown.append(colorControls, colorForm);
|
149
|
+
input.before(pickerBtn);
|
150
|
+
parent.append(pickerDropdown);
|
151
|
+
|
152
|
+
// set colour key menu template
|
153
|
+
if (colorKeywords || colorPresets) {
|
192
154
|
const presetsDropdown = createElement({
|
193
155
|
tagName: 'div',
|
194
|
-
className: `color-dropdown menu${dropClass}`,
|
195
|
-
});
|
196
|
-
const presetsMenu = createElement({
|
197
|
-
tagName: 'ul',
|
198
|
-
ariaLabel: `${menuLabel}`,
|
199
|
-
className: 'color-menu',
|
200
|
-
});
|
201
|
-
setAttribute(presetsMenu, 'role', 'listbox');
|
202
|
-
presetsDropdown.append(presetsMenu);
|
203
|
-
|
204
|
-
colorKeys.forEach((x) => {
|
205
|
-
const xKey = x.trim();
|
206
|
-
const xRealColor = new Color(xKey, { format }).toString();
|
207
|
-
const isActive = xRealColor === getAttribute(input, 'value');
|
208
|
-
const active = isActive ? ' active' : '';
|
209
|
-
|
210
|
-
const keyOption = createElement({
|
211
|
-
tagName: 'li',
|
212
|
-
className: `color-option${active}`,
|
213
|
-
ariaSelected: isActive ? 'true' : 'false',
|
214
|
-
innerText: `${x}`,
|
215
|
-
});
|
216
|
-
setAttribute(keyOption, 'role', 'option');
|
217
|
-
setAttribute(keyOption, 'tabindex', '0');
|
218
|
-
setAttribute(keyOption, 'data-value', `${xKey}`);
|
219
|
-
presetsMenu.append(keyOption);
|
156
|
+
className: `color-dropdown scrollable menu${dropClass}`,
|
220
157
|
});
|
158
|
+
|
159
|
+
// color presets
|
160
|
+
if ((colorPresets instanceof Array && colorPresets.length)
|
161
|
+
|| (colorPresets instanceof ColorPalette && colorPresets.colors)) {
|
162
|
+
const presetsMenu = getColorMenu(self, colorPresets, 'color-options');
|
163
|
+
presetsDropdown.append(presetsMenu);
|
164
|
+
}
|
165
|
+
|
166
|
+
// explicit defaults [reset, initial, inherit, transparent, currentColor]
|
167
|
+
if (colorKeywords && colorKeywords.length) {
|
168
|
+
const keywordsMenu = getColorMenu(self, colorKeywords, 'color-defaults');
|
169
|
+
presetsDropdown.append(keywordsMenu);
|
170
|
+
}
|
171
|
+
|
221
172
|
const presetsBtn = createElement({
|
222
173
|
tagName: 'button',
|
223
|
-
className: 'menu-toggle
|
224
|
-
ariaExpanded: 'false',
|
225
|
-
ariaHasPopup: 'true',
|
174
|
+
className: 'menu-toggle btn-appearance',
|
226
175
|
});
|
176
|
+
setAttribute(presetsBtn, tabIndex, '-1');
|
177
|
+
setAttribute(presetsBtn, ariaExpanded, 'false');
|
178
|
+
setAttribute(presetsBtn, ariaHasPopup, 'true');
|
179
|
+
|
227
180
|
const xmlns = encodeURI('http://www.w3.org/2000/svg');
|
228
181
|
const presetsIcon = createElementNS(xmlns, { tagName: 'svg' });
|
229
182
|
setAttribute(presetsIcon, 'xmlns', xmlns);
|
230
|
-
setAttribute(presetsIcon, ariaHidden, 'true');
|
231
183
|
setAttribute(presetsIcon, 'viewBox', '0 0 512 512');
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
184
|
+
setAttribute(presetsIcon, ariaHidden, 'true');
|
185
|
+
|
186
|
+
const path = createElementNS(xmlns, { tagName: 'path' });
|
187
|
+
setAttribute(path, 'd', 'M98,158l157,156L411,158l27,27L255,368L71,185L98,158z');
|
188
|
+
setAttribute(path, 'fill', '#fff');
|
189
|
+
presetsIcon.append(path);
|
236
190
|
presetsBtn.append(createElement({
|
237
191
|
tagName: 'span',
|
238
192
|
className: vHidden,
|
@@ -243,9 +197,29 @@ function initCallback(self) {
|
|
243
197
|
}
|
244
198
|
|
245
199
|
// solve non-colors after settings save
|
246
|
-
if (
|
200
|
+
if (colorKeywords && nonColors.includes(colorValue)) {
|
247
201
|
self.value = colorValue;
|
248
202
|
}
|
203
|
+
setAttribute(input, tabIndex, '-1');
|
204
|
+
}
|
205
|
+
|
206
|
+
/**
|
207
|
+
* Add / remove `ColorPicker` main event listeners.
|
208
|
+
* @param {ColorPicker} self
|
209
|
+
* @param {boolean=} action
|
210
|
+
*/
|
211
|
+
function toggleEvents(self, action) {
|
212
|
+
const fn = action ? addListener : removeListener;
|
213
|
+
const { input, pickerToggle, menuToggle } = self;
|
214
|
+
|
215
|
+
fn(input, focusinEvent, self.showPicker);
|
216
|
+
fn(pickerToggle, mouseclickEvent, self.togglePicker);
|
217
|
+
|
218
|
+
fn(input, keydownEvent, self.keyToggle);
|
219
|
+
|
220
|
+
if (menuToggle) {
|
221
|
+
fn(menuToggle, mouseclickEvent, self.toggleMenu);
|
222
|
+
}
|
249
223
|
}
|
250
224
|
|
251
225
|
/**
|
@@ -255,26 +229,33 @@ function initCallback(self) {
|
|
255
229
|
*/
|
256
230
|
function toggleEventsOnShown(self, action) {
|
257
231
|
const fn = action ? addListener : removeListener;
|
258
|
-
const
|
259
|
-
|
260
|
-
|
232
|
+
const { input, colorMenu, parent } = self;
|
233
|
+
const doc = getDocument(input);
|
234
|
+
const win = getWindow(input);
|
235
|
+
const pointerEvents = `on${touchstartEvent}` in doc
|
236
|
+
? { down: touchstartEvent, move: touchmoveEvent, up: touchendEvent }
|
237
|
+
: { down: mousedownEvent, move: mousemoveEvent, up: mouseupEvent };
|
261
238
|
|
262
239
|
fn(self.controls, pointerEvents.down, self.pointerDown);
|
263
|
-
self.controlKnobs.forEach((x) => fn(x,
|
240
|
+
self.controlKnobs.forEach((x) => fn(x, keydownEvent, self.handleKnobs));
|
264
241
|
|
265
|
-
|
242
|
+
// @ts-ignore -- this is `Window`
|
243
|
+
fn(win, scrollEvent, self.handleScroll);
|
244
|
+
// @ts-ignore -- this is `Window`
|
245
|
+
fn(win, resizeEvent, self.update);
|
266
246
|
|
267
|
-
[
|
247
|
+
[input, ...self.inputs].forEach((x) => fn(x, changeEvent, self.changeHandler));
|
268
248
|
|
269
|
-
if (
|
270
|
-
fn(
|
271
|
-
fn(
|
249
|
+
if (colorMenu) {
|
250
|
+
fn(colorMenu, mouseclickEvent, self.menuClickHandler);
|
251
|
+
fn(colorMenu, keydownEvent, self.menuKeyHandler);
|
272
252
|
}
|
273
253
|
|
274
|
-
fn(
|
275
|
-
fn(
|
276
|
-
fn(
|
277
|
-
|
254
|
+
fn(doc, pointerEvents.move, self.pointerMove);
|
255
|
+
fn(doc, pointerEvents.up, self.pointerUp);
|
256
|
+
fn(parent, focusoutEvent, self.handleFocusOut);
|
257
|
+
// @ts-ignore -- this is `Window`
|
258
|
+
fn(win, keyupEvent, self.handleDismiss);
|
278
259
|
}
|
279
260
|
|
280
261
|
/**
|
@@ -286,61 +267,93 @@ function firePickerChange(self) {
|
|
286
267
|
}
|
287
268
|
|
288
269
|
/**
|
289
|
-
*
|
270
|
+
* Hides a visible dropdown.
|
290
271
|
* @param {HTMLElement} element
|
291
|
-
* @
|
292
|
-
* @returns {void | boolean}
|
272
|
+
* @returns {void}
|
293
273
|
*/
|
294
|
-
function
|
295
|
-
const fn1 = !check ? 'forEach' : 'some';
|
296
|
-
const fn2 = !check ? removeClass : hasClass;
|
297
|
-
|
274
|
+
function removePosition(element) {
|
298
275
|
if (element) {
|
299
|
-
|
276
|
+
['bottom', 'top'].forEach((x) => removeClass(element, x));
|
300
277
|
}
|
301
|
-
|
302
|
-
return false;
|
303
278
|
}
|
304
279
|
|
305
280
|
/**
|
306
|
-
* Shows
|
281
|
+
* Shows a `ColorPicker` dropdown and close the curent open dropdown.
|
307
282
|
* @param {ColorPicker} self
|
283
|
+
* @param {HTMLElement | Element} dropdown
|
308
284
|
*/
|
309
|
-
function
|
310
|
-
|
311
|
-
|
312
|
-
self
|
313
|
-
|
285
|
+
function showDropdown(self, dropdown) {
|
286
|
+
const {
|
287
|
+
colorPicker, colorMenu, menuToggle, pickerToggle, parent,
|
288
|
+
} = self;
|
289
|
+
const isPicker = dropdown === colorPicker;
|
290
|
+
const openDropdown = isPicker ? colorMenu : colorPicker;
|
291
|
+
const activeBtn = isPicker ? menuToggle : pickerToggle;
|
292
|
+
const nextBtn = !isPicker ? menuToggle : pickerToggle;
|
293
|
+
|
294
|
+
if (!hasClass(parent, 'open')) {
|
295
|
+
addClass(parent, 'open');
|
296
|
+
}
|
297
|
+
if (openDropdown) {
|
298
|
+
removeClass(openDropdown, 'show');
|
299
|
+
removePosition(openDropdown);
|
300
|
+
}
|
301
|
+
addClass(dropdown, 'bottom');
|
302
|
+
reflow(dropdown);
|
303
|
+
addClass(dropdown, 'show');
|
304
|
+
|
305
|
+
if (isPicker) self.update();
|
306
|
+
|
307
|
+
if (!self.isOpen) {
|
308
|
+
toggleEventsOnShown(self, true);
|
309
|
+
self.updateDropdownPosition();
|
310
|
+
self.isOpen = true;
|
311
|
+
setAttribute(self.input, tabIndex, '0');
|
312
|
+
if (menuToggle) {
|
313
|
+
setAttribute(menuToggle, tabIndex, '0');
|
314
|
+
}
|
315
|
+
}
|
316
|
+
|
317
|
+
setAttribute(nextBtn, ariaExpanded, 'true');
|
318
|
+
if (activeBtn) {
|
319
|
+
setAttribute(activeBtn, ariaExpanded, 'false');
|
320
|
+
}
|
314
321
|
}
|
315
322
|
|
316
323
|
/**
|
317
|
-
* Color Picker
|
324
|
+
* Color Picker Web Component
|
318
325
|
* @see http://thednp.github.io/color-picker
|
319
326
|
*/
|
320
327
|
export default class ColorPicker {
|
321
328
|
/**
|
322
|
-
* Returns a new ColorPicker instance.
|
329
|
+
* Returns a new `ColorPicker` instance. The target of this constructor
|
330
|
+
* must be an `HTMLInputElement`.
|
331
|
+
*
|
323
332
|
* @param {HTMLInputElement | string} target the target `<input>` element
|
333
|
+
* @param {CP.ColorPickerOptions=} config instance options
|
324
334
|
*/
|
325
|
-
constructor(target) {
|
335
|
+
constructor(target, config) {
|
326
336
|
const self = this;
|
327
337
|
/** @type {HTMLInputElement} */
|
328
338
|
// @ts-ignore
|
329
|
-
|
339
|
+
const input = querySelector(target);
|
340
|
+
|
330
341
|
// invalidate
|
331
|
-
if (!
|
332
|
-
|
342
|
+
if (!input) throw new TypeError(`ColorPicker target ${target} cannot be found.`);
|
343
|
+
self.input = input;
|
344
|
+
|
345
|
+
const parent = closest(input, colorPickerParentSelector);
|
346
|
+
if (!parent) throw new TypeError('ColorPicker requires a specific markup to work.');
|
333
347
|
|
334
348
|
/** @type {HTMLElement} */
|
335
349
|
// @ts-ignore
|
336
|
-
self.parent =
|
337
|
-
if (!self.parent) throw new TypeError('ColorPicker requires a specific markup to work.');
|
350
|
+
self.parent = parent;
|
338
351
|
|
339
352
|
/** @type {number} */
|
340
353
|
self.id = getUID(input, colorPickerString);
|
341
354
|
|
342
355
|
// set initial state
|
343
|
-
/** @type {
|
356
|
+
/** @type {HTMLElement?} */
|
344
357
|
self.dragElement = null;
|
345
358
|
/** @type {boolean} */
|
346
359
|
self.isOpen = false;
|
@@ -350,26 +363,59 @@ export default class ColorPicker {
|
|
350
363
|
};
|
351
364
|
/** @type {Record<string, string>} */
|
352
365
|
self.colorLabels = {};
|
353
|
-
/** @type {
|
354
|
-
self.
|
355
|
-
/** @type {
|
356
|
-
self.
|
366
|
+
/** @type {string[]=} */
|
367
|
+
self.colorKeywords = undefined;
|
368
|
+
/** @type {(ColorPalette | string[])=} */
|
369
|
+
self.colorPresets = undefined;
|
370
|
+
|
371
|
+
// process options
|
372
|
+
const {
|
373
|
+
format, componentLabels, colorLabels, colorKeywords, colorPresets,
|
374
|
+
} = normalizeOptions(this.isCE ? parent : input, colorPickerDefaults, config || {});
|
375
|
+
|
376
|
+
let translatedColorLabels = colorNames;
|
377
|
+
if (colorLabels instanceof Array && colorLabels.length === 17) {
|
378
|
+
translatedColorLabels = colorLabels;
|
379
|
+
} else if (colorLabels && colorLabels.split(',').length === 17) {
|
380
|
+
translatedColorLabels = colorLabels.split(',');
|
381
|
+
}
|
382
|
+
|
383
|
+
// expose colour labels to all methods
|
384
|
+
colorNames.forEach((c, i) => {
|
385
|
+
self.colorLabels[c] = translatedColorLabels[i].trim();
|
386
|
+
});
|
387
|
+
|
388
|
+
// update and expose component labels
|
389
|
+
const tempLabels = ObjectAssign({}, colorPickerLabels);
|
390
|
+
const jsonLabels = componentLabels && isValidJSON(componentLabels)
|
391
|
+
? JSON.parse(componentLabels) : componentLabels || {};
|
392
|
+
|
357
393
|
/** @type {Record<string, string>} */
|
358
|
-
self.componentLabels = ObjectAssign(
|
394
|
+
self.componentLabels = ObjectAssign(tempLabels, jsonLabels);
|
359
395
|
|
360
|
-
|
361
|
-
|
362
|
-
self.componentLabels = ObjectAssign(self.componentLabels, temp);
|
396
|
+
/** @type {Color} */
|
397
|
+
self.color = new Color('white', format);
|
363
398
|
|
364
|
-
|
365
|
-
|
399
|
+
/** @type {CP.ColorFormats} */
|
400
|
+
self.format = format;
|
366
401
|
|
367
|
-
//
|
368
|
-
|
402
|
+
// set colour defaults
|
403
|
+
if (colorKeywords instanceof Array) {
|
404
|
+
self.colorKeywords = colorKeywords;
|
405
|
+
} else if (typeof colorKeywords === 'string' && colorKeywords.length) {
|
406
|
+
self.colorKeywords = colorKeywords.split(',');
|
407
|
+
}
|
369
408
|
|
370
409
|
// set colour presets
|
371
|
-
if (
|
372
|
-
self.
|
410
|
+
if (colorPresets instanceof Array) {
|
411
|
+
self.colorPresets = colorPresets;
|
412
|
+
} else if (typeof colorPresets === 'string' && colorPresets.length) {
|
413
|
+
if (isValidJSON(colorPresets)) {
|
414
|
+
const { hue, hueSteps, lightSteps } = JSON.parse(colorPresets);
|
415
|
+
self.colorPresets = new ColorPalette(hue, hueSteps, lightSteps);
|
416
|
+
} else {
|
417
|
+
self.colorPresets = colorPresets.split(',').map((x) => x.trim());
|
418
|
+
}
|
373
419
|
}
|
374
420
|
|
375
421
|
// bind events
|
@@ -381,17 +427,18 @@ export default class ColorPicker {
|
|
381
427
|
self.pointerDown = self.pointerDown.bind(self);
|
382
428
|
self.pointerMove = self.pointerMove.bind(self);
|
383
429
|
self.pointerUp = self.pointerUp.bind(self);
|
430
|
+
self.update = self.update.bind(self);
|
384
431
|
self.handleScroll = self.handleScroll.bind(self);
|
385
432
|
self.handleFocusOut = self.handleFocusOut.bind(self);
|
386
433
|
self.changeHandler = self.changeHandler.bind(self);
|
387
434
|
self.handleDismiss = self.handleDismiss.bind(self);
|
388
|
-
self.
|
435
|
+
self.keyToggle = self.keyToggle.bind(self);
|
389
436
|
self.handleKnobs = self.handleKnobs.bind(self);
|
390
437
|
|
391
438
|
// generate markup
|
392
439
|
initCallback(self);
|
393
440
|
|
394
|
-
const
|
441
|
+
const [colorPicker, colorMenu] = getElementsByClassName('color-dropdown', parent);
|
395
442
|
// set main elements
|
396
443
|
/** @type {HTMLElement} */
|
397
444
|
// @ts-ignore
|
@@ -401,68 +448,24 @@ export default class ColorPicker {
|
|
401
448
|
self.menuToggle = querySelector('.menu-toggle', parent);
|
402
449
|
/** @type {HTMLElement} */
|
403
450
|
// @ts-ignore
|
404
|
-
self.
|
405
|
-
/** @type {HTMLElement} */
|
406
|
-
// @ts-ignore
|
407
|
-
self.colorPicker = querySelector('.color-dropdown.picker', parent);
|
451
|
+
self.colorPicker = colorPicker;
|
408
452
|
/** @type {HTMLElement} */
|
409
453
|
// @ts-ignore
|
410
|
-
self.
|
454
|
+
self.colorMenu = colorMenu;
|
411
455
|
/** @type {HTMLInputElement[]} */
|
412
456
|
// @ts-ignore
|
413
|
-
self.inputs = [...
|
457
|
+
self.inputs = [...getElementsByClassName('color-input', parent)];
|
458
|
+
const [controls] = getElementsByClassName('color-controls', parent);
|
459
|
+
self.controls = controls;
|
460
|
+
/** @type {(HTMLElement | Element)[]} */
|
461
|
+
self.controlKnobs = [...getElementsByClassName('knob', controls)];
|
414
462
|
/** @type {(HTMLElement)[]} */
|
415
463
|
// @ts-ignore
|
416
|
-
self.
|
417
|
-
/** @type {HTMLCanvasElement[]} */
|
418
|
-
// @ts-ignore
|
419
|
-
self.visuals = [...querySelectorAll('canvas', self.controls)];
|
420
|
-
/** @type {HTMLLabelElement[]} */
|
421
|
-
// @ts-ignore
|
422
|
-
self.knobLabels = [...querySelectorAll('.color-label', parent)];
|
423
|
-
/** @type {HTMLLabelElement} */
|
424
|
-
// @ts-ignore
|
425
|
-
self.appearance = querySelector('.color-appearance', parent);
|
464
|
+
self.visuals = [...getElementsByClassName('visual-control', controls)];
|
426
465
|
|
427
|
-
|
428
|
-
|
429
|
-
/** @type {number} */
|
430
|
-
self.width1 = v1.width;
|
431
|
-
/** @type {number} */
|
432
|
-
self.height1 = v1.height;
|
433
|
-
/** @type {number} */
|
434
|
-
self.width2 = v2.width;
|
435
|
-
/** @type {number} */
|
436
|
-
self.height2 = v2.height;
|
437
|
-
// set main controls
|
438
|
-
/** @type {*} */
|
439
|
-
self.ctx1 = v1.getContext('2d');
|
440
|
-
/** @type {*} */
|
441
|
-
self.ctx2 = v2.getContext('2d');
|
442
|
-
self.ctx1.rect(0, 0, self.width1, self.height1);
|
443
|
-
self.ctx2.rect(0, 0, self.width2, self.height2);
|
444
|
-
|
445
|
-
/** @type {number} */
|
446
|
-
self.width3 = 0;
|
447
|
-
/** @type {number} */
|
448
|
-
self.height3 = 0;
|
449
|
-
|
450
|
-
// set alpha control except hex
|
451
|
-
if (self.format !== 'hex') {
|
452
|
-
self.width3 = v3.width;
|
453
|
-
self.height3 = v3.height;
|
454
|
-
/** @type {*} */
|
455
|
-
this.ctx3 = v3.getContext('2d');
|
456
|
-
self.ctx3.rect(0, 0, self.width3, self.height3);
|
457
|
-
}
|
466
|
+
// update colour picker controls, inputs and visuals
|
467
|
+
self.update();
|
458
468
|
|
459
|
-
// update color picker controls, inputs and visuals
|
460
|
-
this.setControlPositions();
|
461
|
-
this.setColorAppearence();
|
462
|
-
// don't trigger change at initialization
|
463
|
-
this.updateInputs(true);
|
464
|
-
this.updateControls();
|
465
|
-
this.updateVisuals();
|
466
469
|
// add main events listeners
|
467
470
|
toggleEvents(self, true);
|
468
471
|
|
@@ -470,65 +473,52 @@ export default class ColorPicker {
|
|
470
473
|
Data.set(input, colorPickerString, self);
|
471
474
|
}
|
472
475
|
|
473
|
-
/** Returns the current
|
476
|
+
/** Returns the current colour value */
|
474
477
|
get value() { return this.input.value; }
|
475
478
|
|
476
479
|
/**
|
477
|
-
* Sets a new
|
478
|
-
* @param {string} v new
|
480
|
+
* Sets a new colour value.
|
481
|
+
* @param {string} v new colour value
|
479
482
|
*/
|
480
483
|
set value(v) { this.input.value = v; }
|
481
484
|
|
482
|
-
/** Check if the
|
483
|
-
get
|
484
|
-
|
485
|
-
|
486
|
-
* Returns the colour format.
|
487
|
-
* @returns {CP.ColorFormats | string}
|
488
|
-
*/
|
489
|
-
get format() { return getAttribute(this.input, 'format') || 'hex'; }
|
490
|
-
|
491
|
-
/** Returns the input name. */
|
492
|
-
get name() { return getAttribute(this.input, 'name'); }
|
493
|
-
|
494
|
-
/**
|
495
|
-
* Returns the label associated to the input.
|
496
|
-
* @returns {HTMLLabelElement?}
|
497
|
-
*/
|
498
|
-
// @ts-ignore
|
499
|
-
get label() { return querySelector(`[for="${this.input.id}"]`); }
|
500
|
-
|
501
|
-
/** Check if the color presets include any non-color. */
|
502
|
-
get includeNonColor() {
|
503
|
-
return this.keywords instanceof Array
|
504
|
-
&& this.keywords.some((x) => nonColors.includes(x));
|
485
|
+
/** Check if the colour presets include any non-colour. */
|
486
|
+
get hasNonColor() {
|
487
|
+
return this.colorKeywords instanceof Array
|
488
|
+
&& this.colorKeywords.some((x) => nonColors.includes(x));
|
505
489
|
}
|
506
490
|
|
507
|
-
/**
|
508
|
-
get
|
491
|
+
/** Check if the parent of the target is a `ColorPickerElement` instance. */
|
492
|
+
get isCE() { return this.parent.localName === colorPickerString; }
|
509
493
|
|
510
|
-
/** Returns
|
494
|
+
/** Returns hexadecimal value of the current colour. */
|
495
|
+
get hex() { return this.color.toHex(true); }
|
496
|
+
|
497
|
+
/** Returns the current colour value in {h,s,v,a} object format. */
|
511
498
|
get hsv() { return this.color.toHsv(); }
|
512
499
|
|
513
|
-
/** Returns the current
|
500
|
+
/** Returns the current colour value in {h,s,l,a} object format. */
|
514
501
|
get hsl() { return this.color.toHsl(); }
|
515
502
|
|
516
|
-
/** Returns the current
|
503
|
+
/** Returns the current colour value in {h,w,b,a} object format. */
|
504
|
+
get hwb() { return this.color.toHwb(); }
|
505
|
+
|
506
|
+
/** Returns the current colour value in {r,g,b,a} object format. */
|
517
507
|
get rgb() { return this.color.toRgb(); }
|
518
508
|
|
519
|
-
/** Returns the current
|
509
|
+
/** Returns the current colour brightness. */
|
520
510
|
get brightness() { return this.color.brightness; }
|
521
511
|
|
522
|
-
/** Returns the current
|
512
|
+
/** Returns the current colour luminance. */
|
523
513
|
get luminance() { return this.color.luminance; }
|
524
514
|
|
525
|
-
/** Checks if the current colour requires a light text
|
515
|
+
/** Checks if the current colour requires a light text colour. */
|
526
516
|
get isDark() {
|
527
|
-
const {
|
528
|
-
return brightness < 120 &&
|
517
|
+
const { color, brightness } = this;
|
518
|
+
return brightness < 120 && color.a > 0.33;
|
529
519
|
}
|
530
520
|
|
531
|
-
/** Checks if the current input value is a valid
|
521
|
+
/** Checks if the current input value is a valid colour. */
|
532
522
|
get isValid() {
|
533
523
|
const inputValue = this.input.value;
|
534
524
|
return inputValue !== '' && new Color(inputValue).isValid;
|
@@ -538,89 +528,79 @@ export default class ColorPicker {
|
|
538
528
|
updateVisuals() {
|
539
529
|
const self = this;
|
540
530
|
const {
|
541
|
-
|
542
|
-
width1, width2, width3,
|
543
|
-
height1, height2, height3,
|
544
|
-
ctx1, ctx2, ctx3,
|
531
|
+
format, controlPositions, visuals,
|
545
532
|
} = self;
|
546
|
-
const
|
533
|
+
const [v1, v2, v3] = visuals;
|
534
|
+
const { offsetWidth, offsetHeight } = v1;
|
535
|
+
const hue = format === 'hsl'
|
536
|
+
? controlPositions.c1x / offsetWidth
|
537
|
+
: controlPositions.c2y / offsetHeight;
|
538
|
+
// @ts-ignore - `hslToRgb` is assigned to `Color` as static method
|
539
|
+
const { r, g, b } = Color.hslToRgb(hue, 1, 0.5);
|
540
|
+
const whiteGrad = 'linear-gradient(rgb(255,255,255) 0%, rgb(255,255,255) 100%)';
|
541
|
+
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
542
|
+
const roundA = roundPart((alpha * 100)) / 100;
|
547
543
|
|
548
544
|
if (format !== 'hsl') {
|
549
|
-
const
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
ctx1.fillRect(0, 0, width1, height1);
|
564
|
-
|
565
|
-
const hueGrad = ctx2.createLinearGradient(0, 0, 0, height1);
|
566
|
-
hueGrad.addColorStop(0, 'rgba(255,0,0,1)');
|
567
|
-
hueGrad.addColorStop(0.17, 'rgba(255,255,0,1)');
|
568
|
-
hueGrad.addColorStop(0.34, 'rgba(0,255,0,1)');
|
569
|
-
hueGrad.addColorStop(0.51, 'rgba(0,255,255,1)');
|
570
|
-
hueGrad.addColorStop(0.68, 'rgba(0,0,255,1)');
|
571
|
-
hueGrad.addColorStop(0.85, 'rgba(255,0,255,1)');
|
572
|
-
hueGrad.addColorStop(1, 'rgba(255,0,0,1)');
|
573
|
-
ctx2.fillStyle = hueGrad;
|
574
|
-
ctx2.fillRect(0, 0, width2, height2);
|
545
|
+
const fill = new Color({
|
546
|
+
h: hue, s: 1, l: 0.5, a: alpha,
|
547
|
+
}).toRgbString();
|
548
|
+
const hueGradient = `linear-gradient(
|
549
|
+
rgb(255,0,0) 0%, rgb(255,255,0) 16.67%,
|
550
|
+
rgb(0,255,0) 33.33%, rgb(0,255,255) 50%,
|
551
|
+
rgb(0,0,255) 66.67%, rgb(255,0,255) 83.33%,
|
552
|
+
rgb(255,0,0) 100%)`;
|
553
|
+
setElementStyle(v1, {
|
554
|
+
background: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,${roundA}) 100%),
|
555
|
+
linear-gradient(to right, rgba(255,255,255,${roundA}) 0%, ${fill} 100%),
|
556
|
+
${whiteGrad}`,
|
557
|
+
});
|
558
|
+
setElementStyle(v2, { background: hueGradient });
|
575
559
|
} else {
|
576
|
-
const
|
577
|
-
const
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
if (format !== 'hex') {
|
613
|
-
ctx3.clearRect(0, 0, width3, height3);
|
614
|
-
const alphaGrad = ctx3.createLinearGradient(0, 0, 0, height3);
|
615
|
-
alphaGrad.addColorStop(0, `rgba(${r},${g},${b},1)`);
|
616
|
-
alphaGrad.addColorStop(1, `rgba(${r},${g},${b},0)`);
|
617
|
-
ctx3.fillStyle = alphaGrad;
|
618
|
-
ctx3.fillRect(0, 0, width3, height3);
|
560
|
+
const saturation = roundPart((controlPositions.c2y / offsetHeight) * 100);
|
561
|
+
const fill0 = new Color({
|
562
|
+
r: 255, g: 0, b: 0, a: alpha,
|
563
|
+
}).saturate(-saturation).toRgbString();
|
564
|
+
const fill1 = new Color({
|
565
|
+
r: 255, g: 255, b: 0, a: alpha,
|
566
|
+
}).saturate(-saturation).toRgbString();
|
567
|
+
const fill2 = new Color({
|
568
|
+
r: 0, g: 255, b: 0, a: alpha,
|
569
|
+
}).saturate(-saturation).toRgbString();
|
570
|
+
const fill3 = new Color({
|
571
|
+
r: 0, g: 255, b: 255, a: alpha,
|
572
|
+
}).saturate(-saturation).toRgbString();
|
573
|
+
const fill4 = new Color({
|
574
|
+
r: 0, g: 0, b: 255, a: alpha,
|
575
|
+
}).saturate(-saturation).toRgbString();
|
576
|
+
const fill5 = new Color({
|
577
|
+
r: 255, g: 0, b: 255, a: alpha,
|
578
|
+
}).saturate(-saturation).toRgbString();
|
579
|
+
const fill6 = new Color({
|
580
|
+
r: 255, g: 0, b: 0, a: alpha,
|
581
|
+
}).saturate(-saturation).toRgbString();
|
582
|
+
const fillGradient = `linear-gradient(to right,
|
583
|
+
${fill0} 0%, ${fill1} 16.67%, ${fill2} 33.33%, ${fill3} 50%,
|
584
|
+
${fill4} 66.67%, ${fill5} 83.33%, ${fill6} 100%)`;
|
585
|
+
const lightGrad = `linear-gradient(rgba(255,255,255,${roundA}) 0%, rgba(255,255,255,0) 50%),
|
586
|
+
linear-gradient(rgba(0,0,0,0) 50%, rgba(0,0,0,${roundA}) 100%)`;
|
587
|
+
|
588
|
+
setElementStyle(v1, { background: `${lightGrad},${fillGradient},${whiteGrad}` });
|
589
|
+
const {
|
590
|
+
r: gr, g: gg, b: gb,
|
591
|
+
} = new Color({ r, g, b }).greyscale().toRgb();
|
592
|
+
|
593
|
+
setElementStyle(v2, {
|
594
|
+
background: `linear-gradient(rgb(${r},${g},${b}) 0%, rgb(${gr},${gg},${gb}) 100%)`,
|
595
|
+
});
|
619
596
|
}
|
597
|
+
setElementStyle(v3, {
|
598
|
+
background: `linear-gradient(rgba(${r},${g},${b},1) 0%,rgba(${r},${g},${b},0) 100%)`,
|
599
|
+
});
|
620
600
|
}
|
621
601
|
|
622
602
|
/**
|
623
|
-
*
|
603
|
+
* The `ColorPicker` *focusout* event listener when open.
|
624
604
|
* @param {FocusEvent} e
|
625
605
|
* @this {ColorPicker}
|
626
606
|
*/
|
@@ -632,7 +612,7 @@ export default class ColorPicker {
|
|
632
612
|
}
|
633
613
|
|
634
614
|
/**
|
635
|
-
*
|
615
|
+
* The `ColorPicker` *keyup* event listener when open.
|
636
616
|
* @param {KeyboardEvent} e
|
637
617
|
* @this {ColorPicker}
|
638
618
|
*/
|
@@ -644,14 +624,13 @@ export default class ColorPicker {
|
|
644
624
|
}
|
645
625
|
|
646
626
|
/**
|
647
|
-
*
|
627
|
+
* The `ColorPicker` *scroll* event listener when open.
|
648
628
|
* @param {Event} e
|
649
629
|
* @this {ColorPicker}
|
650
630
|
*/
|
651
631
|
handleScroll(e) {
|
652
632
|
const self = this;
|
653
|
-
|
654
|
-
const { activeElement } = document;
|
633
|
+
const { activeElement } = getDocument(self.input);
|
655
634
|
|
656
635
|
if ((isMobile && self.dragElement)
|
657
636
|
|| (activeElement && self.controlKnobs.includes(activeElement))) {
|
@@ -663,22 +642,51 @@ export default class ColorPicker {
|
|
663
642
|
}
|
664
643
|
|
665
644
|
/**
|
666
|
-
*
|
645
|
+
* The `ColorPicker` keyboard event listener for menu navigation.
|
667
646
|
* @param {KeyboardEvent} e
|
668
647
|
* @this {ColorPicker}
|
669
648
|
*/
|
670
649
|
menuKeyHandler(e) {
|
671
650
|
const { target, code } = e;
|
672
|
-
|
673
|
-
|
651
|
+
// @ts-ignore
|
652
|
+
const { previousElementSibling, nextElementSibling, parentElement } = target;
|
653
|
+
const isColorOptionsMenu = parentElement && hasClass(parentElement, 'color-options');
|
654
|
+
const allSiblings = [...parentElement.children];
|
655
|
+
const columnsCount = isColorOptionsMenu
|
656
|
+
&& getElementStyle(parentElement, 'grid-template-columns').split(' ').length;
|
657
|
+
const currentIndex = allSiblings.indexOf(target);
|
658
|
+
const previousElement = currentIndex > -1
|
659
|
+
&& columnsCount && allSiblings[currentIndex - columnsCount];
|
660
|
+
const nextElement = currentIndex > -1
|
661
|
+
&& columnsCount && allSiblings[currentIndex + columnsCount];
|
662
|
+
|
663
|
+
if ([keyArrowDown, keyArrowUp, keySpace].includes(code)) {
|
664
|
+
// prevent scroll when navigating the menu via arrow keys / Space
|
674
665
|
e.preventDefault();
|
675
|
-
}
|
666
|
+
}
|
667
|
+
if (isColorOptionsMenu) {
|
668
|
+
if (previousElement && code === keyArrowUp) {
|
669
|
+
focus(previousElement);
|
670
|
+
} else if (nextElement && code === keyArrowDown) {
|
671
|
+
focus(nextElement);
|
672
|
+
} else if (previousElementSibling && code === keyArrowLeft) {
|
673
|
+
focus(previousElementSibling);
|
674
|
+
} else if (nextElementSibling && code === keyArrowRight) {
|
675
|
+
focus(nextElementSibling);
|
676
|
+
}
|
677
|
+
} else if (previousElementSibling && [keyArrowLeft, keyArrowUp].includes(code)) {
|
678
|
+
focus(previousElementSibling);
|
679
|
+
} else if (nextElementSibling && [keyArrowRight, keyArrowDown].includes(code)) {
|
680
|
+
focus(nextElementSibling);
|
681
|
+
}
|
682
|
+
|
683
|
+
if ([keyEnter, keySpace].includes(code)) {
|
676
684
|
this.menuClickHandler({ target });
|
677
685
|
}
|
678
686
|
}
|
679
687
|
|
680
688
|
/**
|
681
|
-
*
|
689
|
+
* The `ColorPicker` click event listener for the colour menu presets / defaults.
|
682
690
|
* @param {Partial<Event>} e
|
683
691
|
* @this {ColorPicker}
|
684
692
|
*/
|
@@ -686,51 +694,57 @@ export default class ColorPicker {
|
|
686
694
|
const self = this;
|
687
695
|
/** @type {*} */
|
688
696
|
const { target } = e;
|
689
|
-
const {
|
697
|
+
const { colorMenu } = self;
|
690
698
|
const newOption = (getAttribute(target, 'data-value') || '').trim();
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
self.updateInputs(true);
|
697
|
-
self.updateControls();
|
698
|
-
self.updateVisuals();
|
699
|
+
// invalidate for targets other than color options
|
700
|
+
if (!newOption.length) return;
|
701
|
+
const currentActive = querySelector('li.active', colorMenu);
|
702
|
+
let newColor = nonColors.includes(newOption) ? 'white' : newOption;
|
703
|
+
newColor = newOption === 'transparent' ? 'rgba(0,0,0,0)' : newOption;
|
699
704
|
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
705
|
+
const {
|
706
|
+
r, g, b, a,
|
707
|
+
} = new Color(newColor);
|
708
|
+
|
709
|
+
ObjectAssign(self.color, {
|
710
|
+
r, g, b, a,
|
711
|
+
});
|
712
|
+
|
713
|
+
self.update();
|
704
714
|
|
705
715
|
if (currentActive !== target) {
|
716
|
+
if (currentActive) {
|
717
|
+
removeClass(currentActive, 'active');
|
718
|
+
removeAttribute(currentActive, ariaSelected);
|
719
|
+
}
|
720
|
+
|
706
721
|
addClass(target, 'active');
|
707
722
|
setAttribute(target, ariaSelected, 'true');
|
708
723
|
|
709
724
|
if (nonColors.includes(newOption)) {
|
710
725
|
self.value = newOption;
|
711
|
-
firePickerChange(self);
|
712
726
|
}
|
727
|
+
firePickerChange(self);
|
713
728
|
}
|
714
729
|
}
|
715
730
|
|
716
731
|
/**
|
717
|
-
*
|
732
|
+
* The `ColorPicker` *touchstart* / *mousedown* events listener for control knobs.
|
718
733
|
* @param {TouchEvent} e
|
719
734
|
* @this {ColorPicker}
|
720
735
|
*/
|
721
736
|
pointerDown(e) {
|
722
737
|
const self = this;
|
738
|
+
/** @type {*} */
|
723
739
|
const {
|
724
|
-
// @ts-ignore
|
725
740
|
type, target, touches, pageX, pageY,
|
726
741
|
} = e;
|
727
|
-
const { visuals, controlKnobs
|
742
|
+
const { colorMenu, visuals, controlKnobs } = self;
|
728
743
|
const [v1, v2, v3] = visuals;
|
729
744
|
const [c1, c2, c3] = controlKnobs;
|
730
|
-
/** @type {
|
731
|
-
|
732
|
-
|
733
|
-
? target : querySelector('canvas', target.parentElement);
|
745
|
+
/** @type {HTMLElement} */
|
746
|
+
const visual = hasClass(target, 'visual-control')
|
747
|
+
? target : querySelector('.visual-control', target.parentElement);
|
734
748
|
const visualRect = getBoundingClientRect(visual);
|
735
749
|
const X = type === 'touchstart' ? touches[0].pageX : pageX;
|
736
750
|
const Y = type === 'touchstart' ? touches[0].pageY : pageY;
|
@@ -739,42 +753,53 @@ export default class ColorPicker {
|
|
739
753
|
|
740
754
|
if (target === v1 || target === c1) {
|
741
755
|
self.dragElement = visual;
|
742
|
-
self.changeControl1(
|
756
|
+
self.changeControl1(offsetX, offsetY);
|
743
757
|
} else if (target === v2 || target === c2) {
|
744
758
|
self.dragElement = visual;
|
745
|
-
self.changeControl2(
|
746
|
-
} else if (
|
759
|
+
self.changeControl2(offsetY);
|
760
|
+
} else if (target === v3 || target === c3) {
|
747
761
|
self.dragElement = visual;
|
748
|
-
self.changeAlpha(
|
762
|
+
self.changeAlpha(offsetY);
|
763
|
+
}
|
764
|
+
|
765
|
+
if (colorMenu) {
|
766
|
+
const currentActive = querySelector('li.active', colorMenu);
|
767
|
+
if (currentActive) {
|
768
|
+
removeClass(currentActive, 'active');
|
769
|
+
removeAttribute(currentActive, ariaSelected);
|
770
|
+
}
|
749
771
|
}
|
750
772
|
e.preventDefault();
|
751
773
|
}
|
752
774
|
|
753
775
|
/**
|
754
|
-
*
|
776
|
+
* The `ColorPicker` *touchend* / *mouseup* events listener for control knobs.
|
755
777
|
* @param {TouchEvent} e
|
756
778
|
* @this {ColorPicker}
|
757
779
|
*/
|
758
780
|
pointerUp({ target }) {
|
759
781
|
const self = this;
|
760
|
-
const
|
782
|
+
const { parent } = self;
|
783
|
+
const doc = getDocument(parent);
|
784
|
+
const currentOpen = querySelector(`${colorPickerParentSelector}.open`, doc) !== null;
|
785
|
+
const selection = doc.getSelection();
|
761
786
|
// @ts-ignore
|
762
787
|
if (!self.dragElement && !selection.toString().length
|
763
788
|
// @ts-ignore
|
764
|
-
&& !
|
765
|
-
self.hide();
|
789
|
+
&& !parent.contains(target)) {
|
790
|
+
self.hide(currentOpen);
|
766
791
|
}
|
767
792
|
|
768
793
|
self.dragElement = null;
|
769
794
|
}
|
770
795
|
|
771
796
|
/**
|
772
|
-
*
|
797
|
+
* The `ColorPicker` *touchmove* / *mousemove* events listener for control knobs.
|
773
798
|
* @param {TouchEvent} e
|
774
799
|
*/
|
775
800
|
pointerMove(e) {
|
776
801
|
const self = this;
|
777
|
-
const { dragElement, visuals
|
802
|
+
const { dragElement, visuals } = self;
|
778
803
|
const [v1, v2, v3] = visuals;
|
779
804
|
const {
|
780
805
|
// @ts-ignore
|
@@ -790,20 +815,20 @@ export default class ColorPicker {
|
|
790
815
|
const offsetY = Y - window.pageYOffset - controlRect.top;
|
791
816
|
|
792
817
|
if (dragElement === v1) {
|
793
|
-
self.changeControl1(
|
818
|
+
self.changeControl1(offsetX, offsetY);
|
794
819
|
}
|
795
820
|
|
796
821
|
if (dragElement === v2) {
|
797
|
-
self.changeControl2(
|
822
|
+
self.changeControl2(offsetY);
|
798
823
|
}
|
799
824
|
|
800
|
-
if (dragElement === v3
|
801
|
-
self.changeAlpha(
|
825
|
+
if (dragElement === v3) {
|
826
|
+
self.changeAlpha(offsetY);
|
802
827
|
}
|
803
828
|
}
|
804
829
|
|
805
830
|
/**
|
806
|
-
*
|
831
|
+
* The `ColorPicker` *keydown* event listener for control knobs.
|
807
832
|
* @param {KeyboardEvent} e
|
808
833
|
*/
|
809
834
|
handleKnobs(e) {
|
@@ -814,54 +839,64 @@ export default class ColorPicker {
|
|
814
839
|
if (![keyArrowUp, keyArrowDown, keyArrowLeft, keyArrowRight].includes(code)) return;
|
815
840
|
e.preventDefault();
|
816
841
|
|
817
|
-
const {
|
818
|
-
const {
|
819
|
-
const currentKnob = controlKnobs.find((x) => x === activeElement);
|
842
|
+
const { format, controlKnobs, visuals } = self;
|
843
|
+
const { offsetWidth, offsetHeight } = visuals[0];
|
820
844
|
const [c1, c2, c3] = controlKnobs;
|
845
|
+
const { activeElement } = getDocument(c1);
|
846
|
+
const currentKnob = controlKnobs.find((x) => x === activeElement);
|
847
|
+
const yRatio = offsetHeight / (format === 'hsl' ? 100 : 360);
|
821
848
|
|
822
849
|
if (currentKnob) {
|
823
850
|
let offsetX = 0;
|
824
851
|
let offsetY = 0;
|
852
|
+
|
825
853
|
if (target === c1) {
|
854
|
+
const xRatio = offsetWidth / (format === 'hsl' ? 360 : 100);
|
855
|
+
|
826
856
|
if ([keyArrowLeft, keyArrowRight].includes(code)) {
|
827
|
-
self.controlPositions.c1x += code === keyArrowRight ?
|
857
|
+
self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
|
828
858
|
} else if ([keyArrowUp, keyArrowDown].includes(code)) {
|
829
|
-
self.controlPositions.c1y += code === keyArrowDown ?
|
859
|
+
self.controlPositions.c1y += code === keyArrowDown ? yRatio : -yRatio;
|
830
860
|
}
|
831
861
|
|
832
862
|
offsetX = self.controlPositions.c1x;
|
833
863
|
offsetY = self.controlPositions.c1y;
|
834
|
-
self.changeControl1(
|
864
|
+
self.changeControl1(offsetX, offsetY);
|
835
865
|
} else if (target === c2) {
|
836
|
-
self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code)
|
866
|
+
self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code)
|
867
|
+
? yRatio
|
868
|
+
: -yRatio;
|
869
|
+
|
837
870
|
offsetY = self.controlPositions.c2y;
|
838
|
-
self.changeControl2(
|
871
|
+
self.changeControl2(offsetY);
|
839
872
|
} else if (target === c3) {
|
840
|
-
self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code)
|
873
|
+
self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code)
|
874
|
+
? yRatio
|
875
|
+
: -yRatio;
|
876
|
+
|
841
877
|
offsetY = self.controlPositions.c3y;
|
842
|
-
self.changeAlpha(
|
878
|
+
self.changeAlpha(offsetY);
|
843
879
|
}
|
844
|
-
|
845
|
-
self.setColorAppearence();
|
846
|
-
self.updateInputs();
|
847
|
-
self.updateControls();
|
848
|
-
self.updateVisuals();
|
849
880
|
self.handleScroll(e);
|
850
881
|
}
|
851
882
|
}
|
852
883
|
|
853
|
-
/**
|
884
|
+
/** The event listener of the colour form inputs. */
|
854
885
|
changeHandler() {
|
855
886
|
const self = this;
|
856
887
|
let colorSource;
|
857
|
-
/** @type {HTMLInputElement} */
|
858
|
-
// @ts-ignore
|
859
|
-
const { activeElement } = document;
|
860
888
|
const {
|
861
|
-
inputs, format, value: currentValue, input,
|
889
|
+
inputs, format, value: currentValue, input, controlPositions, visuals,
|
862
890
|
} = self;
|
863
|
-
|
864
|
-
const
|
891
|
+
/** @type {*} */
|
892
|
+
const { activeElement } = getDocument(input);
|
893
|
+
const { offsetHeight } = visuals[0];
|
894
|
+
const [i1,,, i4] = inputs;
|
895
|
+
const [v1, v2, v3, v4] = format === 'rgb'
|
896
|
+
? inputs.map((i) => parseFloat(i.value) / (i === i4 ? 100 : 1))
|
897
|
+
: inputs.map((i) => parseFloat(i.value) / (i !== i1 ? 100 : 360));
|
898
|
+
const isNonColorValue = self.hasNonColor && nonColors.includes(currentValue);
|
899
|
+
const alpha = i4 ? v4 : (1 - controlPositions.c3y / offsetHeight);
|
865
900
|
|
866
901
|
if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
|
867
902
|
if (activeElement === input) {
|
@@ -873,14 +908,28 @@ export default class ColorPicker {
|
|
873
908
|
} else if (format === 'hex') {
|
874
909
|
colorSource = i1.value;
|
875
910
|
} else if (format === 'hsl') {
|
876
|
-
colorSource =
|
911
|
+
colorSource = {
|
912
|
+
h: v1, s: v2, l: v3, a: alpha,
|
913
|
+
};
|
914
|
+
} else if (format === 'hwb') {
|
915
|
+
colorSource = {
|
916
|
+
h: v1, w: v2, b: v3, a: alpha,
|
917
|
+
};
|
877
918
|
} else {
|
878
|
-
colorSource =
|
919
|
+
colorSource = {
|
920
|
+
r: v1, g: v2, b: v3, a: alpha,
|
921
|
+
};
|
879
922
|
}
|
880
923
|
|
881
|
-
|
924
|
+
const {
|
925
|
+
r, g, b, a,
|
926
|
+
} = new Color(colorSource);
|
927
|
+
|
928
|
+
ObjectAssign(self.color, {
|
929
|
+
r, g, b, a,
|
930
|
+
});
|
882
931
|
self.setControlPositions();
|
883
|
-
self.
|
932
|
+
self.updateAppearance();
|
884
933
|
self.updateInputs();
|
885
934
|
self.updateControls();
|
886
935
|
self.updateVisuals();
|
@@ -897,49 +946,57 @@ export default class ColorPicker {
|
|
897
946
|
* * `lightness` and `saturation` for HEX/RGB;
|
898
947
|
* * `lightness` and `hue` for HSL.
|
899
948
|
*
|
900
|
-
* @param {
|
949
|
+
* @param {number} X the X component of the offset
|
950
|
+
* @param {number} Y the Y component of the offset
|
901
951
|
*/
|
902
|
-
changeControl1(
|
952
|
+
changeControl1(X, Y) {
|
903
953
|
const self = this;
|
904
954
|
let [offsetX, offsetY] = [0, 0];
|
905
|
-
const { offsetX: X, offsetY: Y } = offsets;
|
906
955
|
const {
|
907
|
-
format, controlPositions,
|
908
|
-
height1, height2, height3, width1,
|
956
|
+
format, controlPositions, visuals,
|
909
957
|
} = self;
|
958
|
+
const { offsetHeight, offsetWidth } = visuals[0];
|
910
959
|
|
911
|
-
if (X >
|
912
|
-
|
913
|
-
} else if (X >= 0) {
|
914
|
-
offsetX = X;
|
915
|
-
}
|
960
|
+
if (X > offsetWidth) offsetX = offsetWidth;
|
961
|
+
else if (X >= 0) offsetX = X;
|
916
962
|
|
917
|
-
if (Y >
|
918
|
-
|
919
|
-
} else if (Y >= 0) {
|
920
|
-
offsetY = Y;
|
921
|
-
}
|
963
|
+
if (Y > offsetHeight) offsetY = offsetHeight;
|
964
|
+
else if (Y >= 0) offsetY = Y;
|
922
965
|
|
923
|
-
const hue = format
|
924
|
-
?
|
925
|
-
:
|
966
|
+
const hue = format === 'hsl'
|
967
|
+
? offsetX / offsetWidth
|
968
|
+
: controlPositions.c2y / offsetHeight;
|
926
969
|
|
927
|
-
const saturation = format
|
928
|
-
?
|
929
|
-
:
|
970
|
+
const saturation = format === 'hsl'
|
971
|
+
? 1 - controlPositions.c2y / offsetHeight
|
972
|
+
: offsetX / offsetWidth;
|
930
973
|
|
931
|
-
const lightness =
|
932
|
-
const alpha =
|
933
|
-
|
974
|
+
const lightness = 1 - offsetY / offsetHeight;
|
975
|
+
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
976
|
+
|
977
|
+
const colorObject = format === 'hsl'
|
978
|
+
? {
|
979
|
+
h: hue, s: saturation, l: lightness, a: alpha,
|
980
|
+
}
|
981
|
+
: {
|
982
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
983
|
+
};
|
934
984
|
|
935
985
|
// new color
|
936
|
-
|
986
|
+
const {
|
987
|
+
r, g, b, a,
|
988
|
+
} = new Color(colorObject);
|
989
|
+
|
990
|
+
ObjectAssign(self.color, {
|
991
|
+
r, g, b, a,
|
992
|
+
});
|
993
|
+
|
937
994
|
// new positions
|
938
995
|
self.controlPositions.c1x = offsetX;
|
939
996
|
self.controlPositions.c1y = offsetY;
|
940
997
|
|
941
998
|
// update color picker
|
942
|
-
self.
|
999
|
+
self.updateAppearance();
|
943
1000
|
self.updateInputs();
|
944
1001
|
self.updateControls();
|
945
1002
|
self.updateVisuals();
|
@@ -947,37 +1004,52 @@ export default class ColorPicker {
|
|
947
1004
|
|
948
1005
|
/**
|
949
1006
|
* Updates `ColorPicker` second control:
|
950
|
-
* * `hue` for HEX/RGB;
|
1007
|
+
* * `hue` for HEX/RGB/HWB;
|
951
1008
|
* * `saturation` for HSL.
|
952
1009
|
*
|
953
|
-
* @param {
|
1010
|
+
* @param {number} Y the Y offset
|
954
1011
|
*/
|
955
|
-
changeControl2(
|
1012
|
+
changeControl2(Y) {
|
956
1013
|
const self = this;
|
957
|
-
const { offsetY: Y } = offset;
|
958
1014
|
const {
|
959
|
-
format,
|
1015
|
+
format, controlPositions, visuals,
|
960
1016
|
} = self;
|
961
|
-
|
1017
|
+
const { offsetHeight, offsetWidth } = visuals[0];
|
962
1018
|
|
963
|
-
|
964
|
-
offsetY = height2;
|
965
|
-
} else if (Y >= 0) {
|
966
|
-
offsetY = Y;
|
967
|
-
}
|
1019
|
+
let offsetY = 0;
|
968
1020
|
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
const
|
973
|
-
|
1021
|
+
if (Y > offsetHeight) offsetY = offsetHeight;
|
1022
|
+
else if (Y >= 0) offsetY = Y;
|
1023
|
+
|
1024
|
+
const hue = format === 'hsl'
|
1025
|
+
? controlPositions.c1x / offsetWidth
|
1026
|
+
: offsetY / offsetHeight;
|
1027
|
+
const saturation = format === 'hsl'
|
1028
|
+
? 1 - offsetY / offsetHeight
|
1029
|
+
: controlPositions.c1x / offsetWidth;
|
1030
|
+
const lightness = 1 - controlPositions.c1y / offsetHeight;
|
1031
|
+
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
1032
|
+
const colorObject = format === 'hsl'
|
1033
|
+
? {
|
1034
|
+
h: hue, s: saturation, l: lightness, a: alpha,
|
1035
|
+
}
|
1036
|
+
: {
|
1037
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
1038
|
+
};
|
974
1039
|
|
975
1040
|
// new color
|
976
|
-
|
1041
|
+
const {
|
1042
|
+
r, g, b, a,
|
1043
|
+
} = new Color(colorObject);
|
1044
|
+
|
1045
|
+
ObjectAssign(self.color, {
|
1046
|
+
r, g, b, a,
|
1047
|
+
});
|
1048
|
+
|
977
1049
|
// new position
|
978
1050
|
self.controlPositions.c2y = offsetY;
|
979
1051
|
// update color picker
|
980
|
-
self.
|
1052
|
+
self.updateAppearance();
|
981
1053
|
self.updateInputs();
|
982
1054
|
self.updateControls();
|
983
1055
|
self.updateVisuals();
|
@@ -985,95 +1057,108 @@ export default class ColorPicker {
|
|
985
1057
|
|
986
1058
|
/**
|
987
1059
|
* Updates `ColorPicker` last control,
|
988
|
-
* the `alpha` channel
|
1060
|
+
* the `alpha` channel.
|
989
1061
|
*
|
990
|
-
* @param {
|
1062
|
+
* @param {number} Y
|
991
1063
|
*/
|
992
|
-
changeAlpha(
|
1064
|
+
changeAlpha(Y) {
|
993
1065
|
const self = this;
|
994
|
-
const {
|
995
|
-
const {
|
1066
|
+
const { visuals } = self;
|
1067
|
+
const { offsetHeight } = visuals[0];
|
996
1068
|
let offsetY = 0;
|
997
1069
|
|
998
|
-
if (Y >
|
999
|
-
|
1000
|
-
} else if (Y >= 0) {
|
1001
|
-
offsetY = Y;
|
1002
|
-
}
|
1070
|
+
if (Y > offsetHeight) offsetY = offsetHeight;
|
1071
|
+
else if (Y >= 0) offsetY = Y;
|
1003
1072
|
|
1004
1073
|
// update color alpha
|
1005
|
-
const alpha =
|
1006
|
-
self.color.setAlpha(alpha
|
1074
|
+
const alpha = 1 - offsetY / offsetHeight;
|
1075
|
+
self.color.setAlpha(alpha);
|
1007
1076
|
// update position
|
1008
1077
|
self.controlPositions.c3y = offsetY;
|
1009
1078
|
// update color picker
|
1079
|
+
self.updateAppearance();
|
1010
1080
|
self.updateInputs();
|
1011
1081
|
self.updateControls();
|
1012
|
-
// alpha?
|
1013
1082
|
self.updateVisuals();
|
1014
1083
|
}
|
1015
1084
|
|
1016
|
-
/**
|
1085
|
+
/**
|
1086
|
+
* Updates `ColorPicker` control positions on:
|
1087
|
+
* * initialization
|
1088
|
+
* * window resize
|
1089
|
+
*/
|
1090
|
+
update() {
|
1091
|
+
const self = this;
|
1092
|
+
self.updateDropdownPosition();
|
1093
|
+
self.updateAppearance();
|
1094
|
+
self.setControlPositions();
|
1095
|
+
self.updateInputs(true);
|
1096
|
+
self.updateControls();
|
1097
|
+
self.updateVisuals();
|
1098
|
+
}
|
1099
|
+
|
1100
|
+
/** Updates the open dropdown position on *scroll* event. */
|
1017
1101
|
updateDropdownPosition() {
|
1018
1102
|
const self = this;
|
1019
1103
|
const { input, colorPicker, colorMenu } = self;
|
1020
1104
|
const elRect = getBoundingClientRect(input);
|
1105
|
+
const { top, bottom } = elRect;
|
1021
1106
|
const { offsetHeight: elHeight } = input;
|
1022
|
-
const windowHeight =
|
1023
|
-
const isPicker =
|
1107
|
+
const windowHeight = getDocumentElement(input).clientHeight;
|
1108
|
+
const isPicker = hasClass(colorPicker, 'show');
|
1024
1109
|
const dropdown = isPicker ? colorPicker : colorMenu;
|
1110
|
+
if (!dropdown) return;
|
1025
1111
|
const { offsetHeight: dropHeight } = dropdown;
|
1026
|
-
const distanceBottom = windowHeight -
|
1027
|
-
const distanceTop =
|
1028
|
-
const bottomExceed =
|
1029
|
-
const topExceed =
|
1030
|
-
|
1031
|
-
if (hasClass(dropdown, '
|
1032
|
-
removeClass(dropdown, '
|
1033
|
-
addClass(dropdown, '
|
1034
|
-
}
|
1035
|
-
|
1036
|
-
|
1037
|
-
addClass(dropdown, 'show');
|
1112
|
+
const distanceBottom = windowHeight - bottom;
|
1113
|
+
const distanceTop = top;
|
1114
|
+
const bottomExceed = top + dropHeight + elHeight > windowHeight; // show
|
1115
|
+
const topExceed = top - dropHeight < 0; // show-top
|
1116
|
+
|
1117
|
+
if ((hasClass(dropdown, 'bottom') || !topExceed) && distanceBottom < distanceTop && bottomExceed) {
|
1118
|
+
removeClass(dropdown, 'bottom');
|
1119
|
+
addClass(dropdown, 'top');
|
1120
|
+
} else {
|
1121
|
+
removeClass(dropdown, 'top');
|
1122
|
+
addClass(dropdown, 'bottom');
|
1038
1123
|
}
|
1039
1124
|
}
|
1040
1125
|
|
1041
|
-
/**
|
1126
|
+
/** Updates control knobs' positions. */
|
1042
1127
|
setControlPositions() {
|
1043
1128
|
const self = this;
|
1044
1129
|
const {
|
1045
|
-
|
1130
|
+
format, visuals, color, hsl, hsv,
|
1046
1131
|
} = self;
|
1132
|
+
const { offsetHeight, offsetWidth } = visuals[0];
|
1133
|
+
const alpha = color.a;
|
1047
1134
|
const hue = hsl.h;
|
1135
|
+
|
1048
1136
|
const saturation = format !== 'hsl' ? hsv.s : hsl.s;
|
1049
1137
|
const lightness = format !== 'hsl' ? hsv.v : hsl.l;
|
1050
|
-
const alpha = hsv.a;
|
1051
1138
|
|
1052
|
-
self.controlPositions.c1x = format !== 'hsl' ? saturation *
|
1053
|
-
self.controlPositions.c1y = (1 - lightness) *
|
1054
|
-
self.controlPositions.c2y = format !== 'hsl' ?
|
1055
|
-
|
1056
|
-
if (format !== 'hex') {
|
1057
|
-
self.controlPositions.c3y = (1 - alpha) * height3;
|
1058
|
-
}
|
1139
|
+
self.controlPositions.c1x = format !== 'hsl' ? saturation * offsetWidth : hue * offsetWidth;
|
1140
|
+
self.controlPositions.c1y = (1 - lightness) * offsetHeight;
|
1141
|
+
self.controlPositions.c2y = format !== 'hsl' ? hue * offsetHeight : (1 - saturation) * offsetHeight;
|
1142
|
+
self.controlPositions.c3y = (1 - alpha) * offsetHeight;
|
1059
1143
|
}
|
1060
1144
|
|
1061
|
-
/** Update the visual appearance label. */
|
1062
|
-
|
1145
|
+
/** Update the visual appearance label and control knob labels. */
|
1146
|
+
updateAppearance() {
|
1063
1147
|
const self = this;
|
1064
1148
|
const {
|
1065
|
-
componentLabels, colorLabels,
|
1149
|
+
componentLabels, colorLabels, color, parent,
|
1150
|
+
hsl, hsv, hex, format, controlKnobs,
|
1066
1151
|
} = self;
|
1067
1152
|
const {
|
1068
|
-
|
1153
|
+
appearanceLabel, hexLabel, valueLabel,
|
1069
1154
|
} = componentLabels;
|
1070
|
-
|
1071
|
-
const [
|
1072
|
-
const hue =
|
1073
|
-
const alpha =
|
1155
|
+
const { r, g, b } = color.toRgb();
|
1156
|
+
const [knob1, knob2, knob3] = controlKnobs;
|
1157
|
+
const hue = roundPart(hsl.h * 360);
|
1158
|
+
const alpha = color.a;
|
1074
1159
|
const saturationSource = format === 'hsl' ? hsl.s : hsv.s;
|
1075
|
-
const saturation =
|
1076
|
-
const lightness =
|
1160
|
+
const saturation = roundPart(saturationSource * 100);
|
1161
|
+
const lightness = roundPart(hsl.l * 100);
|
1077
1162
|
const hsvl = hsv.v * 100;
|
1078
1163
|
let colorName;
|
1079
1164
|
|
@@ -1109,99 +1194,118 @@ export default class ColorPicker {
|
|
1109
1194
|
colorName = colorLabels.pink;
|
1110
1195
|
}
|
1111
1196
|
|
1197
|
+
let colorLabel = `${hexLabel} ${hex.split('').join(' ')}`;
|
1198
|
+
|
1112
1199
|
if (format === 'hsl') {
|
1113
|
-
|
1114
|
-
|
1200
|
+
colorLabel = `HSL: ${hue}°, ${saturation}%, ${lightness}%`;
|
1201
|
+
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
1202
|
+
setAttribute(knob1, ariaValueText, `${hue}° & ${lightness}%`);
|
1203
|
+
setAttribute(knob1, ariaValueNow, `${hue}`);
|
1204
|
+
setAttribute(knob2, ariaValueText, `${saturation}%`);
|
1205
|
+
setAttribute(knob2, ariaValueNow, `${saturation}`);
|
1206
|
+
} else if (format === 'hwb') {
|
1207
|
+
const { hwb } = self;
|
1208
|
+
const whiteness = roundPart(hwb.w * 100);
|
1209
|
+
const blackness = roundPart(hwb.b * 100);
|
1210
|
+
colorLabel = `HWB: ${hue}°, ${whiteness}%, ${blackness}%`;
|
1211
|
+
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
1212
|
+
setAttribute(knob1, ariaValueText, `${whiteness}% & ${blackness}%`);
|
1213
|
+
setAttribute(knob1, ariaValueNow, `${whiteness}`);
|
1214
|
+
setAttribute(knob2, ariaValueText, `${hue}%`);
|
1215
|
+
setAttribute(knob2, ariaValueNow, `${hue}`);
|
1115
1216
|
} else {
|
1116
|
-
|
1117
|
-
|
1217
|
+
colorLabel = format === 'rgb' ? `RGB: ${r}, ${g}, ${b}` : colorLabel;
|
1218
|
+
setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
1219
|
+
setAttribute(knob1, ariaValueText, `${lightness}% & ${saturation}%`);
|
1220
|
+
setAttribute(knob1, ariaValueNow, `${lightness}`);
|
1221
|
+
setAttribute(knob2, ariaValueText, `${hue}°`);
|
1222
|
+
setAttribute(knob2, ariaValueNow, `${hue}`);
|
1118
1223
|
}
|
1119
1224
|
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
}
|
1225
|
+
const alphaValue = roundPart(alpha * 100);
|
1226
|
+
setAttribute(knob3, ariaValueText, `${alphaValue}%`);
|
1227
|
+
setAttribute(knob3, ariaValueNow, `${alphaValue}`);
|
1124
1228
|
|
1125
|
-
// update
|
1126
|
-
|
1127
|
-
|
1128
|
-
? `${hexLabel} ${hex.split('').join(' ')}.`
|
1129
|
-
: self.value.toUpperCase();
|
1229
|
+
// update the input backgroundColor
|
1230
|
+
const newColor = color.toString();
|
1231
|
+
setElementStyle(self.input, { backgroundColor: newColor });
|
1130
1232
|
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1233
|
+
// toggle dark/light classes will also style the placeholder
|
1234
|
+
// dark sets color white, light sets color black
|
1235
|
+
// isDark ? '#000' : '#fff'
|
1236
|
+
if (!self.isDark) {
|
1237
|
+
if (hasClass(parent, 'txt-dark')) removeClass(parent, 'txt-dark');
|
1238
|
+
if (!hasClass(parent, 'txt-light')) addClass(parent, 'txt-light');
|
1239
|
+
} else {
|
1240
|
+
if (hasClass(parent, 'txt-light')) removeClass(parent, 'txt-light');
|
1241
|
+
if (!hasClass(parent, 'txt-dark')) addClass(parent, 'txt-dark');
|
1138
1242
|
}
|
1139
1243
|
}
|
1140
1244
|
|
1141
|
-
/** Updates the control knobs positions. */
|
1245
|
+
/** Updates the control knobs actual positions. */
|
1142
1246
|
updateControls() {
|
1143
|
-
const {
|
1247
|
+
const { controlKnobs, controlPositions } = this;
|
1248
|
+
let {
|
1249
|
+
c1x, c1y, c2y, c3y,
|
1250
|
+
} = controlPositions;
|
1144
1251
|
const [control1, control2, control3] = controlKnobs;
|
1145
|
-
|
1146
|
-
|
1252
|
+
// round control positions
|
1253
|
+
[c1x, c1y, c2y, c3y] = [c1x, c1y, c2y, c3y].map(roundPart);
|
1147
1254
|
|
1148
|
-
|
1149
|
-
|
1150
|
-
}
|
1255
|
+
setElementStyle(control1, { transform: `translate3d(${c1x - 4}px,${c1y - 4}px,0)` });
|
1256
|
+
setElementStyle(control2, { transform: `translate3d(0,${c2y - 4}px,0)` });
|
1257
|
+
setElementStyle(control3, { transform: `translate3d(0,${c3y - 4}px,0)` });
|
1151
1258
|
}
|
1152
1259
|
|
1153
1260
|
/**
|
1154
|
-
*
|
1261
|
+
* Updates all color form inputs.
|
1155
1262
|
* @param {boolean=} isPrevented when `true`, the component original event is prevented
|
1156
1263
|
*/
|
1157
1264
|
updateInputs(isPrevented) {
|
1158
1265
|
const self = this;
|
1159
1266
|
const {
|
1160
|
-
value: oldColor,
|
1267
|
+
value: oldColor, format, inputs, color, hsl,
|
1161
1268
|
} = self;
|
1162
1269
|
const [i1, i2, i3, i4] = inputs;
|
1163
|
-
|
1164
|
-
const
|
1165
|
-
const hue = Math.round(hsl.h);
|
1166
|
-
const saturation = Math.round(hsl.s * 100);
|
1167
|
-
const lightSource = format === 'hsl' ? hsl.l : hsv.v;
|
1168
|
-
const lightness = Math.round(lightSource * 100);
|
1270
|
+
const alpha = roundPart(color.a * 100);
|
1271
|
+
const hue = roundPart(hsl.h * 360);
|
1169
1272
|
let newColor;
|
1170
1273
|
|
1171
1274
|
if (format === 'hex') {
|
1172
|
-
newColor = self.color.toHexString();
|
1275
|
+
newColor = self.color.toHexString(true);
|
1173
1276
|
i1.value = self.hex;
|
1174
1277
|
} else if (format === 'hsl') {
|
1278
|
+
const lightness = roundPart(hsl.l * 100);
|
1279
|
+
const saturation = roundPart(hsl.s * 100);
|
1175
1280
|
newColor = self.color.toHslString();
|
1176
1281
|
i1.value = `${hue}`;
|
1177
1282
|
i2.value = `${saturation}`;
|
1178
1283
|
i3.value = `${lightness}`;
|
1179
1284
|
i4.value = `${alpha}`;
|
1285
|
+
} else if (format === 'hwb') {
|
1286
|
+
const { w, b } = self.hwb;
|
1287
|
+
const whiteness = roundPart(w * 100);
|
1288
|
+
const blackness = roundPart(b * 100);
|
1289
|
+
|
1290
|
+
newColor = self.color.toHwbString();
|
1291
|
+
i1.value = `${hue}`;
|
1292
|
+
i2.value = `${whiteness}`;
|
1293
|
+
i3.value = `${blackness}`;
|
1294
|
+
i4.value = `${alpha}`;
|
1180
1295
|
} else if (format === 'rgb') {
|
1296
|
+
let { r, g, b } = self.rgb;
|
1297
|
+
[r, g, b] = [r, g, b].map(roundPart);
|
1298
|
+
|
1181
1299
|
newColor = self.color.toRgbString();
|
1182
|
-
i1.value = `${
|
1183
|
-
i2.value = `${
|
1184
|
-
i3.value = `${
|
1300
|
+
i1.value = `${r}`;
|
1301
|
+
i2.value = `${g}`;
|
1302
|
+
i3.value = `${b}`;
|
1185
1303
|
i4.value = `${alpha}`;
|
1186
1304
|
}
|
1187
1305
|
|
1188
1306
|
// update the color value
|
1189
1307
|
self.value = `${newColor}`;
|
1190
1308
|
|
1191
|
-
// update the input backgroundColor
|
1192
|
-
ObjectAssign(input.style, { backgroundColor: newColor });
|
1193
|
-
|
1194
|
-
// toggle dark/light classes will also style the placeholder
|
1195
|
-
// dark sets color white, light sets color black
|
1196
|
-
// isDark ? '#000' : '#fff'
|
1197
|
-
if (!self.isDark) {
|
1198
|
-
if (hasClass(parent, 'dark')) removeClass(parent, 'dark');
|
1199
|
-
if (!hasClass(parent, 'light')) addClass(parent, 'light');
|
1200
|
-
} else {
|
1201
|
-
if (hasClass(parent, 'light')) removeClass(parent, 'light');
|
1202
|
-
if (!hasClass(parent, 'dark')) addClass(parent, 'dark');
|
1203
|
-
}
|
1204
|
-
|
1205
1309
|
// don't trigger the custom event unless it's really changed
|
1206
1310
|
if (!isPrevented && newColor !== oldColor) {
|
1207
1311
|
firePickerChange(self);
|
@@ -1209,14 +1313,15 @@ export default class ColorPicker {
|
|
1209
1313
|
}
|
1210
1314
|
|
1211
1315
|
/**
|
1212
|
-
*
|
1316
|
+
* The `Space` & `Enter` keys specific event listener.
|
1317
|
+
* Toggle visibility of the `ColorPicker` / the presets menu, showing one will hide the other.
|
1213
1318
|
* @param {KeyboardEvent} e
|
1214
1319
|
* @this {ColorPicker}
|
1215
1320
|
*/
|
1216
|
-
|
1321
|
+
keyToggle(e) {
|
1217
1322
|
const self = this;
|
1218
1323
|
const { menuToggle } = self;
|
1219
|
-
const { activeElement } =
|
1324
|
+
const { activeElement } = getDocument(menuToggle);
|
1220
1325
|
const { code } = e;
|
1221
1326
|
|
1222
1327
|
if ([keyEnter, keySpace].includes(code)) {
|
@@ -1239,80 +1344,79 @@ export default class ColorPicker {
|
|
1239
1344
|
togglePicker(e) {
|
1240
1345
|
e.preventDefault();
|
1241
1346
|
const self = this;
|
1242
|
-
const
|
1347
|
+
const { colorPicker } = self;
|
1243
1348
|
|
1244
|
-
if (self.isOpen &&
|
1349
|
+
if (self.isOpen && hasClass(colorPicker, 'show')) {
|
1245
1350
|
self.hide(true);
|
1246
1351
|
} else {
|
1247
|
-
self
|
1352
|
+
showDropdown(self, colorPicker);
|
1248
1353
|
}
|
1249
1354
|
}
|
1250
1355
|
|
1251
1356
|
/** Shows the `ColorPicker` dropdown. */
|
1252
1357
|
showPicker() {
|
1253
1358
|
const self = this;
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1359
|
+
const { colorPicker } = self;
|
1360
|
+
|
1361
|
+
if (!['top', 'bottom'].some((c) => hasClass(colorPicker, c))) {
|
1362
|
+
showDropdown(self, colorPicker);
|
1363
|
+
}
|
1259
1364
|
}
|
1260
1365
|
|
1261
1366
|
/** Toggles the visibility of the `ColorPicker` presets menu. */
|
1262
1367
|
toggleMenu() {
|
1263
1368
|
const self = this;
|
1264
|
-
const
|
1369
|
+
const { colorMenu } = self;
|
1265
1370
|
|
1266
|
-
if (self.isOpen &&
|
1371
|
+
if (self.isOpen && hasClass(colorMenu, 'show')) {
|
1267
1372
|
self.hide(true);
|
1268
1373
|
} else {
|
1269
|
-
|
1270
|
-
}
|
1271
|
-
}
|
1272
|
-
|
1273
|
-
/** Show the dropdown. */
|
1274
|
-
show() {
|
1275
|
-
const self = this;
|
1276
|
-
if (!self.isOpen) {
|
1277
|
-
addClass(self.parent, 'open');
|
1278
|
-
toggleEventsOnShown(self, true);
|
1279
|
-
self.updateDropdownPosition();
|
1280
|
-
self.isOpen = true;
|
1374
|
+
showDropdown(self, colorMenu);
|
1281
1375
|
}
|
1282
1376
|
}
|
1283
1377
|
|
1284
1378
|
/**
|
1285
|
-
* Hides the currently
|
1379
|
+
* Hides the currently open `ColorPicker` dropdown.
|
1286
1380
|
* @param {boolean=} focusPrevented
|
1287
1381
|
*/
|
1288
1382
|
hide(focusPrevented) {
|
1289
1383
|
const self = this;
|
1290
1384
|
if (self.isOpen) {
|
1291
|
-
const {
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
if (
|
1300
|
-
|
1301
|
-
setAttribute(
|
1385
|
+
const {
|
1386
|
+
pickerToggle, menuToggle, colorPicker, colorMenu, parent, input,
|
1387
|
+
} = self;
|
1388
|
+
const openPicker = hasClass(colorPicker, 'show');
|
1389
|
+
const openDropdown = openPicker ? colorPicker : colorMenu;
|
1390
|
+
const relatedBtn = openPicker ? pickerToggle : menuToggle;
|
1391
|
+
const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
|
1392
|
+
|
1393
|
+
if (openDropdown) {
|
1394
|
+
removeClass(openDropdown, 'show');
|
1395
|
+
setAttribute(relatedBtn, ariaExpanded, 'false');
|
1396
|
+
setTimeout(() => {
|
1397
|
+
removePosition(openDropdown);
|
1398
|
+
if (!querySelector('.show', parent)) {
|
1399
|
+
removeClass(parent, 'open');
|
1400
|
+
toggleEventsOnShown(self);
|
1401
|
+
self.isOpen = false;
|
1402
|
+
}
|
1403
|
+
}, animationDuration);
|
1302
1404
|
}
|
1303
1405
|
|
1304
1406
|
if (!self.isValid) {
|
1305
1407
|
self.value = self.color.toString();
|
1306
1408
|
}
|
1307
|
-
|
1308
|
-
self.isOpen = false;
|
1309
|
-
|
1310
1409
|
if (!focusPrevented) {
|
1311
|
-
|
1410
|
+
focus(pickerToggle);
|
1411
|
+
}
|
1412
|
+
setAttribute(input, tabIndex, '-1');
|
1413
|
+
if (menuToggle) {
|
1414
|
+
setAttribute(menuToggle, tabIndex, '-1');
|
1312
1415
|
}
|
1313
1416
|
}
|
1314
1417
|
}
|
1315
1418
|
|
1419
|
+
/** Removes `ColorPicker` from target `<input>`. */
|
1316
1420
|
dispose() {
|
1317
1421
|
const self = this;
|
1318
1422
|
const { input, parent } = self;
|
@@ -1321,13 +1425,25 @@ export default class ColorPicker {
|
|
1321
1425
|
[...parent.children].forEach((el) => {
|
1322
1426
|
if (el !== input) el.remove();
|
1323
1427
|
});
|
1428
|
+
|
1429
|
+
removeAttribute(input, tabIndex);
|
1430
|
+
setElementStyle(input, { backgroundColor: '' });
|
1431
|
+
|
1432
|
+
['txt-light', 'txt-dark'].forEach((c) => removeClass(parent, c));
|
1324
1433
|
Data.remove(input, colorPickerString);
|
1325
1434
|
}
|
1326
1435
|
}
|
1327
1436
|
|
1328
1437
|
ObjectAssign(ColorPicker, {
|
1329
1438
|
Color,
|
1439
|
+
ColorPalette,
|
1440
|
+
Version,
|
1330
1441
|
getInstance: getColorPickerInstance,
|
1331
1442
|
init: initColorPicker,
|
1332
1443
|
selector: colorPickerSelector,
|
1444
|
+
// utils important for render
|
1445
|
+
roundPart,
|
1446
|
+
setElementStyle,
|
1447
|
+
setAttribute,
|
1448
|
+
getBoundingClientRect,
|
1333
1449
|
});
|