cx 25.3.1 → 25.4.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.
Files changed (109) hide show
  1. package/dist/charts.css +8 -0
  2. package/dist/charts.js +29 -2
  3. package/dist/manifest.js +630 -630
  4. package/package.json +1 -1
  5. package/src/charts/Legend.d.ts +45 -27
  6. package/src/charts/Legend.js +22 -2
  7. package/src/charts/Legend.scss +8 -0
  8. package/src/charts/LegendEntry.js +128 -128
  9. package/src/charts/LegendEntry.scss +27 -27
  10. package/src/charts/PieChart.d.ts +92 -92
  11. package/src/charts/PieChart.js +1 -0
  12. package/src/charts/axis/Axis.d.ts +113 -113
  13. package/src/charts/axis/Axis.js +280 -280
  14. package/src/charts/axis/CategoryAxis.d.ts +30 -30
  15. package/src/charts/axis/CategoryAxis.js +241 -241
  16. package/src/charts/axis/NumericAxis.js +351 -351
  17. package/src/charts/axis/Stack.js +55 -55
  18. package/src/charts/axis/TimeAxis.d.ts +28 -28
  19. package/src/charts/axis/TimeAxis.js +611 -611
  20. package/src/charts/helpers/PointReducer.js +47 -47
  21. package/src/charts/helpers/SnapPointFinder.js +69 -69
  22. package/src/data/Binding.spec.js +69 -69
  23. package/src/data/Expression.js +229 -229
  24. package/src/data/Expression.spec.js +229 -229
  25. package/src/data/StringTemplate.js +92 -92
  26. package/src/data/StringTemplate.spec.js +132 -132
  27. package/src/data/StructuredSelector.js +132 -132
  28. package/src/data/getAccessor.spec.js +11 -11
  29. package/src/data/getSelector.js +49 -49
  30. package/src/hooks/createLocalStorageRef.d.ts +3 -3
  31. package/src/hooks/createLocalStorageRef.js +20 -20
  32. package/src/index.scss +6 -6
  33. package/src/ui/Culture.d.ts +57 -57
  34. package/src/ui/Culture.js +139 -139
  35. package/src/ui/FocusManager.js +171 -171
  36. package/src/ui/Format.js +108 -108
  37. package/src/ui/HoverSync.js +147 -147
  38. package/src/ui/Instance.d.ts +72 -72
  39. package/src/ui/Instance.js +614 -614
  40. package/src/ui/Repeater.d.ts +61 -61
  41. package/src/ui/index.d.ts +42 -42
  42. package/src/ui/layout/ContentPlaceholder.d.ts +19 -19
  43. package/src/ui/layout/ContentPlaceholder.js +105 -105
  44. package/src/ui/layout/ContentPlaceholder.spec.js +579 -579
  45. package/src/ui/layout/LabelsTopLayout.js +134 -134
  46. package/src/util/date/encodeDate.d.ts +1 -1
  47. package/src/util/date/encodeDate.js +8 -8
  48. package/src/util/date/encodeDateWithTimezoneOffset.d.ts +1 -1
  49. package/src/util/date/index.d.ts +11 -11
  50. package/src/util/date/index.js +11 -11
  51. package/src/util/date/parseDateInvariant.d.ts +3 -3
  52. package/src/util/date/parseDateInvariant.js +20 -20
  53. package/src/util/getSearchQueryPredicate.js +59 -59
  54. package/src/util/index.d.ts +51 -51
  55. package/src/util/index.js +54 -54
  56. package/src/util/isValidIdentifierName.d.ts +1 -1
  57. package/src/util/isValidIdentifierName.js +5 -5
  58. package/src/util/isValidIdentifierName.spec.js +33 -33
  59. package/src/util/scss/add-rules.scss +38 -38
  60. package/src/widgets/CxCredit.scss +37 -37
  61. package/src/widgets/HighlightedSearchText.js +36 -36
  62. package/src/widgets/HighlightedSearchText.scss +18 -18
  63. package/src/widgets/List.scss +91 -91
  64. package/src/widgets/drag-drop/DropZone.js +214 -214
  65. package/src/widgets/form/Calendar.js +618 -618
  66. package/src/widgets/form/Calendar.scss +196 -196
  67. package/src/widgets/form/Checkbox.scss +127 -127
  68. package/src/widgets/form/ColorField.js +397 -397
  69. package/src/widgets/form/ColorField.scss +96 -96
  70. package/src/widgets/form/ColorPicker.js +485 -485
  71. package/src/widgets/form/ColorPicker.scss +283 -283
  72. package/src/widgets/form/DateTimeField.js +576 -576
  73. package/src/widgets/form/DateTimePicker.js +392 -392
  74. package/src/widgets/form/LookupField.d.ts +179 -179
  75. package/src/widgets/form/LookupField.scss +219 -219
  76. package/src/widgets/form/MonthField.d.ts +99 -99
  77. package/src/widgets/form/MonthField.js +523 -523
  78. package/src/widgets/form/MonthPicker.d.ts +76 -76
  79. package/src/widgets/form/MonthPicker.js +641 -641
  80. package/src/widgets/form/MonthPicker.scss +118 -118
  81. package/src/widgets/form/NumberField.js +459 -459
  82. package/src/widgets/form/NumberField.scss +61 -61
  83. package/src/widgets/form/Radio.scss +121 -121
  84. package/src/widgets/form/Select.scss +99 -99
  85. package/src/widgets/form/Slider.scss +118 -118
  86. package/src/widgets/form/Switch.scss +140 -140
  87. package/src/widgets/form/TextArea.scss +43 -43
  88. package/src/widgets/form/TextField.js +290 -290
  89. package/src/widgets/form/TextField.scss +55 -55
  90. package/src/widgets/form/UploadButton.d.ts +34 -34
  91. package/src/widgets/form/variables.scss +353 -353
  92. package/src/widgets/grid/Grid.d.ts +442 -442
  93. package/src/widgets/grid/Grid.js +3414 -3414
  94. package/src/widgets/grid/GridRow.js +228 -228
  95. package/src/widgets/grid/TreeNode.d.ts +23 -23
  96. package/src/widgets/grid/TreeNode.scss +88 -88
  97. package/src/widgets/grid/variables.scss +133 -133
  98. package/src/widgets/nav/LinkButton.js +128 -128
  99. package/src/widgets/nav/Menu.scss +74 -74
  100. package/src/widgets/overlay/Dropdown.js +612 -612
  101. package/src/widgets/overlay/FlyweightTooltipTracker.js +39 -39
  102. package/src/widgets/overlay/Overlay.d.ts +73 -73
  103. package/src/widgets/overlay/Window.js +202 -202
  104. package/src/widgets/overlay/captureMouse.js +124 -124
  105. package/src/widgets/overlay/createHotPromiseWindowFactory.d.ts +18 -18
  106. package/src/widgets/overlay/createHotPromiseWindowFactory.js +56 -56
  107. package/src/widgets/overlay/index.d.ts +11 -11
  108. package/src/widgets/overlay/index.js +11 -11
  109. package/src/widgets/variables.scss +144 -144
@@ -1,485 +1,485 @@
1
- import { Widget, VDOM } from "../../ui/Widget";
2
- import { Field } from "./Field";
3
- import { captureMouseOrTouch, getCursorPos } from "../overlay/captureMouse";
4
- import { hslToRgb } from "../../util/color/hslToRgb";
5
- import { rgbToHsl } from "../../util/color/rgbToHsl";
6
- import { rgbToHex } from "../../util/color/rgbToHex";
7
- import { parseColor } from "../../util/color/parseColor";
8
- import { getVendorPrefix } from "../../util/getVendorPrefix";
9
- import { stopPropagation } from "../../util/eventCallbacks";
10
- import { isString } from "../../util/isString";
11
- import { getTopLevelBoundingClientRect } from "../../util/getTopLevelBoundingClientRect";
12
- import PixelPickerIcon from "../icons/pixel-picker";
13
-
14
- //TODO: Increase HSL precision in calculations, round only RGB values
15
- //TODO: Resolve alpha input problems
16
-
17
- export class ColorPicker extends Field {
18
- declareData() {
19
- super.declareData(
20
- {
21
- value: this.emptyValue,
22
- format: undefined,
23
- },
24
- ...arguments,
25
- );
26
- }
27
-
28
- renderInput(context, instance, key) {
29
- return <ColorPickerComponent key={key} instance={instance} />;
30
- }
31
-
32
- handleEvent(eventType, instance, color) {
33
- let { data } = instance;
34
- if (this.reportOn.indexOf(eventType) != -1) {
35
- let value;
36
- switch (data.format) {
37
- default:
38
- case "rgba":
39
- value = `rgba(${color.r.toFixed(0)},${color.g.toFixed(0)},${color.b.toFixed(0)},${
40
- Math.round(color.a * 100) / 100
41
- })`;
42
- break;
43
-
44
- case "hsla":
45
- value = `hsla(${color.h.toFixed(0)},${color.s.toFixed(0)}%,${color.l.toFixed(0)}%,${
46
- Math.round(color.a * 100) / 100
47
- })`;
48
- break;
49
-
50
- case "hex":
51
- value = rgbToHex(color.r, color.g, color.b);
52
- break;
53
- }
54
- instance.set("value", value);
55
- }
56
- }
57
- }
58
-
59
- ColorPicker.prototype.baseClass = "colorpicker";
60
- ColorPicker.prototype.reportOn = "blur change";
61
- ColorPicker.prototype.format = "rgba";
62
-
63
- Widget.alias("color-picker", ColorPicker);
64
-
65
- class ColorPickerComponent extends VDOM.Component {
66
- constructor(props) {
67
- super(props);
68
- this.data = props.instance.data;
69
- try {
70
- this.state = this.parse(props.instance.data.value);
71
- } catch (e) {
72
- //if web colors are used (e.g. red), fallback to the default color
73
- this.state = this.parse(null);
74
- }
75
- }
76
-
77
- UNSAFE_componentWillReceiveProps(props) {
78
- let { data } = props.instance;
79
- let color;
80
- try {
81
- color = this.parse(data.value);
82
- } catch {
83
- color = this.parse(null);
84
- }
85
- if (color.r != this.state.r || color.g != this.state.g || color.b != this.state.b || color.a != this.state.a)
86
- this.setState(color);
87
- }
88
-
89
- parse(color) {
90
- let c = parseColor(color);
91
- if (c == null) {
92
- c = {
93
- type: "rgba",
94
- r: 128,
95
- g: 128,
96
- b: 128,
97
- a: 0,
98
- };
99
- }
100
-
101
- c.a = Math.round(c.a * 100) / 100;
102
-
103
- if (c.type == "rgba") {
104
- let [h, s, l] = rgbToHsl(c.r, c.g, c.b);
105
- return { r: c.r, g: c.g, b: c.b, h, s, l, a: c.a };
106
- }
107
-
108
- if (c.type == "hsla") {
109
- let [r, g, b] = hslToRgb(c.h, c.s, c.l);
110
- r = this.fix255(r);
111
- g = this.fix255(g);
112
- b = this.fix255(b);
113
- return { r: r, g, b, h: c.h, s: c.s, l: c.l, a: c.a };
114
- }
115
-
116
- throw new Error(`Color ${color} parsing failed.`);
117
- }
118
-
119
- render() {
120
- let { h, s, l, a, r, g, b } = this.state;
121
- let { instance } = this.props;
122
- let { widget, data } = instance;
123
- let { CSS, baseClass } = widget;
124
- let hcolor = `hsl(${h},100%,50%)`;
125
- let hsla = `hsla(${h.toFixed(0)},${s.toFixed(0)}%,${l.toFixed(0)}%,${a})`;
126
- let rgba = `rgba(${r.toFixed(0)},${g.toFixed(0)},${b.toFixed(0)},${a})`;
127
- let hex = rgbToHex(r, g, b);
128
- let pixelPicker;
129
-
130
- let alphaGradient = `${getVendorPrefix(
131
- "css",
132
- )}linear-gradient(left, hsla(${h},${s}%,${l}%,0) 0%, hsla(${h},${s}%,${l}%,1) 100%)`;
133
-
134
- if (window.EyeDropper) {
135
- pixelPicker = (
136
- <div
137
- className={CSS.element(baseClass, "pixel-picker")}
138
- onClick={(e) => {
139
- const eyeDropper = new EyeDropper();
140
- eyeDropper
141
- .open()
142
- .then((result) => {
143
- instance.set("value", result.sRGBHex);
144
- })
145
- .catch((e) => {});
146
- }}
147
- >
148
- <PixelPickerIcon />
149
- </div>
150
- );
151
- }
152
-
153
- return (
154
- <div
155
- className={data.classNames}
156
- style={data.style}
157
- onBlur={this.onBlur.bind(this)}
158
- onMouseDown={stopPropagation}
159
- onTouchStart={stopPropagation}
160
- >
161
- <div
162
- className={CSS.element(baseClass, "picker")}
163
- style={{ backgroundColor: hcolor }}
164
- onMouseDown={this.onSLSelect.bind(this)}
165
- onTouchStart={this.onSLSelect.bind(this)}
166
- >
167
- <div
168
- className={CSS.element(baseClass, "indicator")}
169
- style={{
170
- left: `calc(${s}% - 4px)`,
171
- top: `calc(${100 - l}% - 4px)`,
172
- borderColor: `rgba(${r < 128 ? 255 : 0}, ${g < 128 ? 255 : 0}, ${b < 128 ? 255 : 0}, 0.5)`,
173
- }}
174
- />
175
- </div>
176
- <div className={CSS.element(baseClass, "details")}>
177
- <div
178
- className={CSS.element(baseClass, "hue")}
179
- onMouseDown={this.onHueSelect.bind(this)}
180
- onTouchStart={this.onHueSelect.bind(this)}
181
- onWheel={(e) => {
182
- this.onWheel(e, "h", 10);
183
- }}
184
- >
185
- <div
186
- className={CSS.element(baseClass, "indicator")}
187
- style={{
188
- left: `calc(${h / 3.6}% - 2px)`,
189
- }}
190
- />
191
- </div>
192
- <div className={CSS.element(baseClass, "inputs")}>
193
- <label>
194
- {"H "}
195
- <input
196
- value={h.toFixed(0)}
197
- onChange={(e) => {
198
- this.onNumberChange(e, "h");
199
- }}
200
- onWheel={(e) => {
201
- this.onWheel(e, "h", 10);
202
- }}
203
- />
204
- </label>
205
- <label>
206
- {"S "}
207
- <input
208
- value={s.toFixed(0)}
209
- onChange={(e) => {
210
- this.onNumberChange(e, "s");
211
- }}
212
- onWheel={(e) => {
213
- this.onWheel(e, "s", 5);
214
- }}
215
- />
216
- </label>
217
- <label>
218
- {"L "}
219
- <input
220
- value={l.toFixed(0)}
221
- onChange={(e) => {
222
- this.onNumberChange(e, "l");
223
- }}
224
- onWheel={(e) => {
225
- this.onWheel(e, "l", 5);
226
- }}
227
- />
228
- </label>
229
- <label>
230
- {"A "}
231
- <input
232
- value={a}
233
- onChange={(e) => {
234
- this.onNumberChange(e, "a");
235
- }}
236
- onWheel={(e) => {
237
- this.onWheel(e, "a", 0.1);
238
- }}
239
- />
240
- </label>
241
- </div>
242
- <div
243
- className={CSS.element(baseClass, "alpha")}
244
- onMouseDown={this.onAlphaSelect.bind(this)}
245
- onTouchStart={this.onAlphaSelect.bind(this)}
246
- onWheel={(e) => {
247
- this.onWheel(e, "a", 0.1);
248
- }}
249
- >
250
- <div style={{ background: alphaGradient }} />
251
- <div
252
- className={CSS.element(baseClass, "indicator")}
253
- style={{
254
- left: `calc(${a * 100}% - 2px)`,
255
- }}
256
- />
257
- </div>
258
- <div className={CSS.element(baseClass, "inputs")}>
259
- <label>
260
- {"R "}
261
- <input
262
- value={r.toFixed(0)}
263
- onChange={(e) => {
264
- this.onNumberChange(e, "r");
265
- }}
266
- onWheel={(e) => {
267
- this.onWheel(e, "r", 5);
268
- }}
269
- />
270
- </label>
271
- <label>
272
- {"G "}
273
- <input
274
- value={g.toFixed(0)}
275
- onChange={(e) => {
276
- this.onNumberChange(e, "g");
277
- }}
278
- onWheel={(e) => {
279
- this.onWheel(e, "g", 5);
280
- }}
281
- />
282
- </label>
283
- <label>
284
- {"B "}
285
- <input
286
- value={b.toFixed(0)}
287
- onChange={(e) => {
288
- this.onNumberChange(e, "b");
289
- }}
290
- onWheel={(e) => {
291
- this.onWheel(e, "b", 5);
292
- }}
293
- />
294
- </label>
295
- <label>
296
- {"A "}
297
- <input
298
- value={a}
299
- onChange={(e) => {
300
- this.onNumberChange(e, "a");
301
- }}
302
- onWheel={(e) => {
303
- this.onWheel(e, "a", 0.1);
304
- }}
305
- />
306
- </label>
307
- </div>
308
- <div className={CSS.element(baseClass, "preview")}>
309
- <div
310
- style={{
311
- display: "flex",
312
- alignItems: "center",
313
- }}
314
- >
315
- <div
316
- className={CSS.element(baseClass, "color")}
317
- onClick={(e) => {
318
- this.onColorClick(e);
319
- }}
320
- >
321
- <div style={{ backgroundColor: hsla }}></div>
322
- </div>
323
- </div>
324
- <div className={CSS.element(baseClass, "values")}>
325
- <input value={hsla} readOnly />
326
- <input value={rgba} readOnly />
327
- <input value={hex} readOnly />
328
- </div>
329
- {pixelPicker}
330
- </div>
331
- </div>
332
- </div>
333
- );
334
- }
335
-
336
- onColorClick(e) {
337
- let { instance } = this.props;
338
- let { widget } = instance;
339
-
340
- if (widget.onColorClick) instance.invoke("onColorClick", e, instance);
341
- }
342
-
343
- onHueSelect(e) {
344
- e.preventDefault();
345
- e.stopPropagation();
346
-
347
- let el = e.currentTarget;
348
- let bounds = el.getBoundingClientRect();
349
-
350
- let move = (e) => {
351
- let pos = getCursorPos(e);
352
- let x = Math.max(0, Math.min(1, (pos.clientX + 1 - bounds.left) / el.offsetWidth));
353
- this.setColorProp({
354
- h: x * 360,
355
- });
356
- };
357
-
358
- captureMouseOrTouch(e, move);
359
- move(e);
360
- }
361
-
362
- onAlphaSelect(e) {
363
- e.preventDefault();
364
- e.stopPropagation();
365
-
366
- let el = e.currentTarget;
367
- let bounds = getTopLevelBoundingClientRect(el);
368
-
369
- let move = (e) => {
370
- let pos = getCursorPos(e);
371
- let x = Math.max(0, Math.min(1, (pos.clientX + 1 - bounds.left) / el.offsetWidth));
372
- this.setColorProp({
373
- a: x,
374
- });
375
- };
376
-
377
- captureMouseOrTouch(e, move);
378
- move(e);
379
- }
380
-
381
- onSLSelect(e) {
382
- e.preventDefault();
383
- e.stopPropagation();
384
-
385
- let el = e.currentTarget;
386
- let bounds = getTopLevelBoundingClientRect(el);
387
-
388
- let move = (e) => {
389
- let pos = getCursorPos(e);
390
- let x = Math.max(0, Math.min(1, (pos.clientX + 1 - bounds.left) / el.offsetWidth));
391
- let y = Math.max(0, Math.min(1, (pos.clientY + 1 - bounds.top) / el.offsetWidth));
392
- let s = x;
393
- let l = 1 - y;
394
- this.setColorProp({
395
- s: s * 100,
396
- l: l * 100,
397
- });
398
- };
399
-
400
- captureMouseOrTouch(e, move);
401
- move(e);
402
- }
403
-
404
- fix255(v) {
405
- return Math.max(0, Math.min(255, Math.round(v)));
406
- }
407
-
408
- setColorProp(props, value) {
409
- if (isString(props)) {
410
- props = {
411
- [props]: value,
412
- };
413
- }
414
-
415
- let state = { ...this.state };
416
- let fixAlpha = false;
417
-
418
- for (let prop in props) {
419
- value = props[prop];
420
-
421
- switch (prop) {
422
- case "h":
423
- state.h = Math.min(360, Math.max(0, value));
424
- [state.r, state.g, state.b] = hslToRgb(state.h, state.s, state.l);
425
- fixAlpha = true;
426
- break;
427
-
428
- case "s":
429
- state.s = Math.min(100, Math.max(0, value));
430
- [state.r, state.g, state.b] = hslToRgb(state.h, state.s, state.l);
431
- fixAlpha = true;
432
- break;
433
-
434
- case "l":
435
- state.l = Math.min(100, Math.max(0, value));
436
- [state.r, state.g, state.b] = hslToRgb(state.h, state.s, state.l);
437
- fixAlpha = true;
438
- break;
439
-
440
- case "r":
441
- case "g":
442
- case "b":
443
- state[prop] = Math.round(Math.min(255, Math.max(0, value)));
444
- let [h, s, l] = rgbToHsl(state.r, state.g, state.b);
445
- state.h = h;
446
- state.s = s;
447
- state.l = l;
448
- fixAlpha = true;
449
- break;
450
-
451
- case "a":
452
- state.a = Math.round(100 * Math.min(1, Math.max(0, value))) / 100;
453
- break;
454
- }
455
- }
456
-
457
- state.r = this.fix255(state.r);
458
- state.g = this.fix255(state.g);
459
- state.b = this.fix255(state.b);
460
-
461
- if (fixAlpha && state.a === 0) state.a = 1;
462
-
463
- this.setState(state, () => {
464
- this.props.instance.widget.handleEvent("change", this.props.instance, this.state);
465
- });
466
- }
467
-
468
- onNumberChange(e, prop) {
469
- e.preventDefault();
470
- e.stopPropagation();
471
- let number = parseFloat(e.target.value || "0");
472
- this.setColorProp(prop, number);
473
- }
474
-
475
- onWheel(e, prop, delta) {
476
- e.preventDefault();
477
- e.stopPropagation();
478
- let factor = e.deltaY < 0 ? 1 : -1;
479
- this.setColorProp(prop, this.state[prop] + delta * factor);
480
- }
481
-
482
- onBlur() {
483
- this.props.instance.widget.handleEvent("blur", this.props.instance, this.state);
484
- }
485
- }
1
+ import { Widget, VDOM } from "../../ui/Widget";
2
+ import { Field } from "./Field";
3
+ import { captureMouseOrTouch, getCursorPos } from "../overlay/captureMouse";
4
+ import { hslToRgb } from "../../util/color/hslToRgb";
5
+ import { rgbToHsl } from "../../util/color/rgbToHsl";
6
+ import { rgbToHex } from "../../util/color/rgbToHex";
7
+ import { parseColor } from "../../util/color/parseColor";
8
+ import { getVendorPrefix } from "../../util/getVendorPrefix";
9
+ import { stopPropagation } from "../../util/eventCallbacks";
10
+ import { isString } from "../../util/isString";
11
+ import { getTopLevelBoundingClientRect } from "../../util/getTopLevelBoundingClientRect";
12
+ import PixelPickerIcon from "../icons/pixel-picker";
13
+
14
+ //TODO: Increase HSL precision in calculations, round only RGB values
15
+ //TODO: Resolve alpha input problems
16
+
17
+ export class ColorPicker extends Field {
18
+ declareData() {
19
+ super.declareData(
20
+ {
21
+ value: this.emptyValue,
22
+ format: undefined,
23
+ },
24
+ ...arguments,
25
+ );
26
+ }
27
+
28
+ renderInput(context, instance, key) {
29
+ return <ColorPickerComponent key={key} instance={instance} />;
30
+ }
31
+
32
+ handleEvent(eventType, instance, color) {
33
+ let { data } = instance;
34
+ if (this.reportOn.indexOf(eventType) != -1) {
35
+ let value;
36
+ switch (data.format) {
37
+ default:
38
+ case "rgba":
39
+ value = `rgba(${color.r.toFixed(0)},${color.g.toFixed(0)},${color.b.toFixed(0)},${
40
+ Math.round(color.a * 100) / 100
41
+ })`;
42
+ break;
43
+
44
+ case "hsla":
45
+ value = `hsla(${color.h.toFixed(0)},${color.s.toFixed(0)}%,${color.l.toFixed(0)}%,${
46
+ Math.round(color.a * 100) / 100
47
+ })`;
48
+ break;
49
+
50
+ case "hex":
51
+ value = rgbToHex(color.r, color.g, color.b);
52
+ break;
53
+ }
54
+ instance.set("value", value);
55
+ }
56
+ }
57
+ }
58
+
59
+ ColorPicker.prototype.baseClass = "colorpicker";
60
+ ColorPicker.prototype.reportOn = "blur change";
61
+ ColorPicker.prototype.format = "rgba";
62
+
63
+ Widget.alias("color-picker", ColorPicker);
64
+
65
+ class ColorPickerComponent extends VDOM.Component {
66
+ constructor(props) {
67
+ super(props);
68
+ this.data = props.instance.data;
69
+ try {
70
+ this.state = this.parse(props.instance.data.value);
71
+ } catch (e) {
72
+ //if web colors are used (e.g. red), fallback to the default color
73
+ this.state = this.parse(null);
74
+ }
75
+ }
76
+
77
+ UNSAFE_componentWillReceiveProps(props) {
78
+ let { data } = props.instance;
79
+ let color;
80
+ try {
81
+ color = this.parse(data.value);
82
+ } catch {
83
+ color = this.parse(null);
84
+ }
85
+ if (color.r != this.state.r || color.g != this.state.g || color.b != this.state.b || color.a != this.state.a)
86
+ this.setState(color);
87
+ }
88
+
89
+ parse(color) {
90
+ let c = parseColor(color);
91
+ if (c == null) {
92
+ c = {
93
+ type: "rgba",
94
+ r: 128,
95
+ g: 128,
96
+ b: 128,
97
+ a: 0,
98
+ };
99
+ }
100
+
101
+ c.a = Math.round(c.a * 100) / 100;
102
+
103
+ if (c.type == "rgba") {
104
+ let [h, s, l] = rgbToHsl(c.r, c.g, c.b);
105
+ return { r: c.r, g: c.g, b: c.b, h, s, l, a: c.a };
106
+ }
107
+
108
+ if (c.type == "hsla") {
109
+ let [r, g, b] = hslToRgb(c.h, c.s, c.l);
110
+ r = this.fix255(r);
111
+ g = this.fix255(g);
112
+ b = this.fix255(b);
113
+ return { r: r, g, b, h: c.h, s: c.s, l: c.l, a: c.a };
114
+ }
115
+
116
+ throw new Error(`Color ${color} parsing failed.`);
117
+ }
118
+
119
+ render() {
120
+ let { h, s, l, a, r, g, b } = this.state;
121
+ let { instance } = this.props;
122
+ let { widget, data } = instance;
123
+ let { CSS, baseClass } = widget;
124
+ let hcolor = `hsl(${h},100%,50%)`;
125
+ let hsla = `hsla(${h.toFixed(0)},${s.toFixed(0)}%,${l.toFixed(0)}%,${a})`;
126
+ let rgba = `rgba(${r.toFixed(0)},${g.toFixed(0)},${b.toFixed(0)},${a})`;
127
+ let hex = rgbToHex(r, g, b);
128
+ let pixelPicker;
129
+
130
+ let alphaGradient = `${getVendorPrefix(
131
+ "css",
132
+ )}linear-gradient(left, hsla(${h},${s}%,${l}%,0) 0%, hsla(${h},${s}%,${l}%,1) 100%)`;
133
+
134
+ if (window.EyeDropper) {
135
+ pixelPicker = (
136
+ <div
137
+ className={CSS.element(baseClass, "pixel-picker")}
138
+ onClick={(e) => {
139
+ const eyeDropper = new EyeDropper();
140
+ eyeDropper
141
+ .open()
142
+ .then((result) => {
143
+ instance.set("value", result.sRGBHex);
144
+ })
145
+ .catch((e) => {});
146
+ }}
147
+ >
148
+ <PixelPickerIcon />
149
+ </div>
150
+ );
151
+ }
152
+
153
+ return (
154
+ <div
155
+ className={data.classNames}
156
+ style={data.style}
157
+ onBlur={this.onBlur.bind(this)}
158
+ onMouseDown={stopPropagation}
159
+ onTouchStart={stopPropagation}
160
+ >
161
+ <div
162
+ className={CSS.element(baseClass, "picker")}
163
+ style={{ backgroundColor: hcolor }}
164
+ onMouseDown={this.onSLSelect.bind(this)}
165
+ onTouchStart={this.onSLSelect.bind(this)}
166
+ >
167
+ <div
168
+ className={CSS.element(baseClass, "indicator")}
169
+ style={{
170
+ left: `calc(${s}% - 4px)`,
171
+ top: `calc(${100 - l}% - 4px)`,
172
+ borderColor: `rgba(${r < 128 ? 255 : 0}, ${g < 128 ? 255 : 0}, ${b < 128 ? 255 : 0}, 0.5)`,
173
+ }}
174
+ />
175
+ </div>
176
+ <div className={CSS.element(baseClass, "details")}>
177
+ <div
178
+ className={CSS.element(baseClass, "hue")}
179
+ onMouseDown={this.onHueSelect.bind(this)}
180
+ onTouchStart={this.onHueSelect.bind(this)}
181
+ onWheel={(e) => {
182
+ this.onWheel(e, "h", 10);
183
+ }}
184
+ >
185
+ <div
186
+ className={CSS.element(baseClass, "indicator")}
187
+ style={{
188
+ left: `calc(${h / 3.6}% - 2px)`,
189
+ }}
190
+ />
191
+ </div>
192
+ <div className={CSS.element(baseClass, "inputs")}>
193
+ <label>
194
+ {"H "}
195
+ <input
196
+ value={h.toFixed(0)}
197
+ onChange={(e) => {
198
+ this.onNumberChange(e, "h");
199
+ }}
200
+ onWheel={(e) => {
201
+ this.onWheel(e, "h", 10);
202
+ }}
203
+ />
204
+ </label>
205
+ <label>
206
+ {"S "}
207
+ <input
208
+ value={s.toFixed(0)}
209
+ onChange={(e) => {
210
+ this.onNumberChange(e, "s");
211
+ }}
212
+ onWheel={(e) => {
213
+ this.onWheel(e, "s", 5);
214
+ }}
215
+ />
216
+ </label>
217
+ <label>
218
+ {"L "}
219
+ <input
220
+ value={l.toFixed(0)}
221
+ onChange={(e) => {
222
+ this.onNumberChange(e, "l");
223
+ }}
224
+ onWheel={(e) => {
225
+ this.onWheel(e, "l", 5);
226
+ }}
227
+ />
228
+ </label>
229
+ <label>
230
+ {"A "}
231
+ <input
232
+ value={a}
233
+ onChange={(e) => {
234
+ this.onNumberChange(e, "a");
235
+ }}
236
+ onWheel={(e) => {
237
+ this.onWheel(e, "a", 0.1);
238
+ }}
239
+ />
240
+ </label>
241
+ </div>
242
+ <div
243
+ className={CSS.element(baseClass, "alpha")}
244
+ onMouseDown={this.onAlphaSelect.bind(this)}
245
+ onTouchStart={this.onAlphaSelect.bind(this)}
246
+ onWheel={(e) => {
247
+ this.onWheel(e, "a", 0.1);
248
+ }}
249
+ >
250
+ <div style={{ background: alphaGradient }} />
251
+ <div
252
+ className={CSS.element(baseClass, "indicator")}
253
+ style={{
254
+ left: `calc(${a * 100}% - 2px)`,
255
+ }}
256
+ />
257
+ </div>
258
+ <div className={CSS.element(baseClass, "inputs")}>
259
+ <label>
260
+ {"R "}
261
+ <input
262
+ value={r.toFixed(0)}
263
+ onChange={(e) => {
264
+ this.onNumberChange(e, "r");
265
+ }}
266
+ onWheel={(e) => {
267
+ this.onWheel(e, "r", 5);
268
+ }}
269
+ />
270
+ </label>
271
+ <label>
272
+ {"G "}
273
+ <input
274
+ value={g.toFixed(0)}
275
+ onChange={(e) => {
276
+ this.onNumberChange(e, "g");
277
+ }}
278
+ onWheel={(e) => {
279
+ this.onWheel(e, "g", 5);
280
+ }}
281
+ />
282
+ </label>
283
+ <label>
284
+ {"B "}
285
+ <input
286
+ value={b.toFixed(0)}
287
+ onChange={(e) => {
288
+ this.onNumberChange(e, "b");
289
+ }}
290
+ onWheel={(e) => {
291
+ this.onWheel(e, "b", 5);
292
+ }}
293
+ />
294
+ </label>
295
+ <label>
296
+ {"A "}
297
+ <input
298
+ value={a}
299
+ onChange={(e) => {
300
+ this.onNumberChange(e, "a");
301
+ }}
302
+ onWheel={(e) => {
303
+ this.onWheel(e, "a", 0.1);
304
+ }}
305
+ />
306
+ </label>
307
+ </div>
308
+ <div className={CSS.element(baseClass, "preview")}>
309
+ <div
310
+ style={{
311
+ display: "flex",
312
+ alignItems: "center",
313
+ }}
314
+ >
315
+ <div
316
+ className={CSS.element(baseClass, "color")}
317
+ onClick={(e) => {
318
+ this.onColorClick(e);
319
+ }}
320
+ >
321
+ <div style={{ backgroundColor: hsla }}></div>
322
+ </div>
323
+ </div>
324
+ <div className={CSS.element(baseClass, "values")}>
325
+ <input value={hsla} readOnly />
326
+ <input value={rgba} readOnly />
327
+ <input value={hex} readOnly />
328
+ </div>
329
+ {pixelPicker}
330
+ </div>
331
+ </div>
332
+ </div>
333
+ );
334
+ }
335
+
336
+ onColorClick(e) {
337
+ let { instance } = this.props;
338
+ let { widget } = instance;
339
+
340
+ if (widget.onColorClick) instance.invoke("onColorClick", e, instance);
341
+ }
342
+
343
+ onHueSelect(e) {
344
+ e.preventDefault();
345
+ e.stopPropagation();
346
+
347
+ let el = e.currentTarget;
348
+ let bounds = el.getBoundingClientRect();
349
+
350
+ let move = (e) => {
351
+ let pos = getCursorPos(e);
352
+ let x = Math.max(0, Math.min(1, (pos.clientX + 1 - bounds.left) / el.offsetWidth));
353
+ this.setColorProp({
354
+ h: x * 360,
355
+ });
356
+ };
357
+
358
+ captureMouseOrTouch(e, move);
359
+ move(e);
360
+ }
361
+
362
+ onAlphaSelect(e) {
363
+ e.preventDefault();
364
+ e.stopPropagation();
365
+
366
+ let el = e.currentTarget;
367
+ let bounds = getTopLevelBoundingClientRect(el);
368
+
369
+ let move = (e) => {
370
+ let pos = getCursorPos(e);
371
+ let x = Math.max(0, Math.min(1, (pos.clientX + 1 - bounds.left) / el.offsetWidth));
372
+ this.setColorProp({
373
+ a: x,
374
+ });
375
+ };
376
+
377
+ captureMouseOrTouch(e, move);
378
+ move(e);
379
+ }
380
+
381
+ onSLSelect(e) {
382
+ e.preventDefault();
383
+ e.stopPropagation();
384
+
385
+ let el = e.currentTarget;
386
+ let bounds = getTopLevelBoundingClientRect(el);
387
+
388
+ let move = (e) => {
389
+ let pos = getCursorPos(e);
390
+ let x = Math.max(0, Math.min(1, (pos.clientX + 1 - bounds.left) / el.offsetWidth));
391
+ let y = Math.max(0, Math.min(1, (pos.clientY + 1 - bounds.top) / el.offsetWidth));
392
+ let s = x;
393
+ let l = 1 - y;
394
+ this.setColorProp({
395
+ s: s * 100,
396
+ l: l * 100,
397
+ });
398
+ };
399
+
400
+ captureMouseOrTouch(e, move);
401
+ move(e);
402
+ }
403
+
404
+ fix255(v) {
405
+ return Math.max(0, Math.min(255, Math.round(v)));
406
+ }
407
+
408
+ setColorProp(props, value) {
409
+ if (isString(props)) {
410
+ props = {
411
+ [props]: value,
412
+ };
413
+ }
414
+
415
+ let state = { ...this.state };
416
+ let fixAlpha = false;
417
+
418
+ for (let prop in props) {
419
+ value = props[prop];
420
+
421
+ switch (prop) {
422
+ case "h":
423
+ state.h = Math.min(360, Math.max(0, value));
424
+ [state.r, state.g, state.b] = hslToRgb(state.h, state.s, state.l);
425
+ fixAlpha = true;
426
+ break;
427
+
428
+ case "s":
429
+ state.s = Math.min(100, Math.max(0, value));
430
+ [state.r, state.g, state.b] = hslToRgb(state.h, state.s, state.l);
431
+ fixAlpha = true;
432
+ break;
433
+
434
+ case "l":
435
+ state.l = Math.min(100, Math.max(0, value));
436
+ [state.r, state.g, state.b] = hslToRgb(state.h, state.s, state.l);
437
+ fixAlpha = true;
438
+ break;
439
+
440
+ case "r":
441
+ case "g":
442
+ case "b":
443
+ state[prop] = Math.round(Math.min(255, Math.max(0, value)));
444
+ let [h, s, l] = rgbToHsl(state.r, state.g, state.b);
445
+ state.h = h;
446
+ state.s = s;
447
+ state.l = l;
448
+ fixAlpha = true;
449
+ break;
450
+
451
+ case "a":
452
+ state.a = Math.round(100 * Math.min(1, Math.max(0, value))) / 100;
453
+ break;
454
+ }
455
+ }
456
+
457
+ state.r = this.fix255(state.r);
458
+ state.g = this.fix255(state.g);
459
+ state.b = this.fix255(state.b);
460
+
461
+ if (fixAlpha && state.a === 0) state.a = 1;
462
+
463
+ this.setState(state, () => {
464
+ this.props.instance.widget.handleEvent("change", this.props.instance, this.state);
465
+ });
466
+ }
467
+
468
+ onNumberChange(e, prop) {
469
+ e.preventDefault();
470
+ e.stopPropagation();
471
+ let number = parseFloat(e.target.value || "0");
472
+ this.setColorProp(prop, number);
473
+ }
474
+
475
+ onWheel(e, prop, delta) {
476
+ e.preventDefault();
477
+ e.stopPropagation();
478
+ let factor = e.deltaY < 0 ? 1 : -1;
479
+ this.setColorProp(prop, this.state[prop] + delta * factor);
480
+ }
481
+
482
+ onBlur() {
483
+ this.props.instance.widget.handleEvent("blur", this.props.instance, this.state);
484
+ }
485
+ }