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