@hpcc-js/other 3.5.4 → 3.5.6

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.
@@ -1,762 +1,762 @@
1
- import { HTMLWidget, Platform, PropertyExt, Widget } from "@hpcc-js/common";
2
- import { Grid } from "@hpcc-js/layout";
3
- import { local as d3Local, select as d3Select, selectAll as d3SelectAll } from "d3-selection";
4
- import * as Persist from "./Persist.ts";
5
-
6
- import "../src/PropertyEditor.css";
7
-
8
- function hasProperties(type) {
9
- switch (type) {
10
- case "widget":
11
- case "widgetArray":
12
- case "propertyArray":
13
- return true;
14
- default:
15
- }
16
- return false;
17
- }
18
-
19
- export class PropertyEditor extends HTMLWidget {
20
- _widgetOrig;
21
- _parentPropertyEditor;
22
- _show_settings: boolean;
23
- _selectedItems;
24
- __meta_sorting;
25
- _watch;
26
- private _childPE = d3Local<PropertyEditor>();
27
-
28
- constructor() {
29
- super();
30
- this._parentPropertyEditor = null;
31
-
32
- this._tag = "div";
33
- this._show_settings = false;
34
- }
35
-
36
- parentPropertyEditor(_?: PropertyEditor): PropertyEditor {
37
- if (!arguments.length) return this._parentPropertyEditor;
38
- this._parentPropertyEditor = _;
39
- return this;
40
- }
41
-
42
- depth(): number {
43
- let retVal = 0;
44
- let parent = this.parentPropertyEditor();
45
- while (parent) {
46
- ++retVal;
47
- parent = parent.parentPropertyEditor();
48
- }
49
- return retVal;
50
- }
51
-
52
- _show_header = true;
53
- show_header(): boolean;
54
- show_header(_: boolean): PropertyEditor;
55
- show_header(_?: boolean): boolean | PropertyEditor {
56
- if (!arguments.length) {
57
- return this._show_header;
58
- }
59
- this._show_header = _;
60
- return this;
61
- }
62
-
63
- show_settings(): boolean;
64
- show_settings(_: boolean): PropertyEditor;
65
- show_settings(_?: boolean): boolean | PropertyEditor {
66
- if (!arguments.length) {
67
- return this._show_settings;
68
- }
69
- this._show_settings = _;
70
- return this;
71
- }
72
-
73
- rootWidgets() {
74
- if (this._selectedItems && this._selectedItems.length) {
75
- return this._selectedItems;
76
- }
77
- return this.show_settings() ? [this] : this.widget() ? [this.widget()] : [];
78
- }
79
-
80
- update(domNode, element) {
81
- super.update(domNode, element);
82
-
83
- const context = this;
84
-
85
- const rootWidgets = this.rootWidgets().filter(function (w) {
86
- if (w._owningWidget && w._owningWidget.excludeObjs instanceof Array) {
87
- if (w._owningWidget.excludeObjs.indexOf(w.classID()) !== -1) {
88
- return false;
89
- }
90
- }
91
- return true;
92
- });
93
-
94
- const table = element.selectAll(`table.property-table.table-${this.depth()}`).data(rootWidgets, function (d) {
95
- // We reuse the existing DOM Nodes and this node _might_ have been a regular Input previously ---
96
- if (typeof d.id !== "function") {
97
- return `meta-${d.id}`;
98
- }
99
- return d.id();
100
- });
101
- table.enter().append("table")
102
- .attr("class", `property-table table-${this.depth()}`)
103
- .each(function () {
104
- const tableElement = d3Select(this);
105
-
106
- // Header ---
107
- if (context._show_header && context.parentPropertyEditor() === null) {
108
- tableElement.append("thead").append("tr").append("th")// .datum(tableElement)
109
- .attr("colspan", "2")
110
- .each(function () {
111
- context.enterHeader(d3Select(this));
112
- })
113
- ;
114
- }
115
-
116
- // Body ---
117
- tableElement.append("tbody");
118
- })
119
- .merge(table)
120
- .each(function (tableData) {
121
- const tableElement = d3Select(this);
122
-
123
- // Header ---
124
- if (context._show_header && context.parentPropertyEditor() === null) {
125
- context.updateHeader(tableElement.select("thead > tr > th"));
126
- }
127
-
128
- // Body ---
129
- context.renderInputs(tableElement.select("tbody"), tableData);
130
- })
131
- ;
132
- table.exit()
133
- .each(function () {
134
- context.renderInputs(element.select("tbody"), null);
135
- })
136
- .remove()
137
- ;
138
- }
139
-
140
- exit(domNode, element) {
141
- super.exit(domNode, element);
142
- this.watchWidget(null);
143
- }
144
-
145
- private watchDepth = 0;
146
- watchWidget(widget) {
147
- if (this._watch) {
148
- if ((window as any).__hpcc_debug) {
149
- --this.watchDepth;
150
- console.info("watchDepth: " + this.watchDepth);
151
- }
152
- this._watch.remove();
153
- delete this._watch;
154
- }
155
- if (widget) {
156
- const context = this;
157
- this._watch = widget.monitor(function (_paramId, newVal, oldVal) {
158
- if (oldVal !== newVal) {
159
- const propEditor = context.parentPropertyEditor() || context;
160
- propEditor.lazyRender();
161
- }
162
- });
163
- if ((window as any).__hpcc_debug) {
164
- ++this.watchDepth;
165
- console.info("watchDepth: " + this.watchDepth);
166
- }
167
- }
168
- }
169
-
170
- enterHeader(th) {
171
- const context = this;
172
-
173
- th.append("span");
174
- th.append("i")
175
- .attr("class", "expandIcon fa")
176
- .on("click", function () {
177
- switch (context.peInputIcon()) {
178
- case "fa-caret-up":
179
- case "fa-caret-right":
180
- context.element().selectAll(`.table-${context.depth()} > tbody > tr > .headerRow > .peInput > .property-table-collapsed`)
181
- .classed("property-table-collapsed", false)
182
- ;
183
- context.element().selectAll(`.table-${context.depth()} > tbody > tr > .headerRow > .peInput > i`)
184
- .classed("fa-minus-square-o", true)
185
- .classed("fa-plus-square-o", false)
186
- ;
187
- break;
188
- case "fa-caret-down":
189
- context.element().selectAll(`.table-${context.depth()} > tbody > tr > .headerRow > .peInput > div`)
190
- .classed("property-table-collapsed", true)
191
- ;
192
- context.element().selectAll(`.table-${context.depth()} > tbody > tr > .headerRow > .peInput > i`)
193
- .classed("fa-minus-square-o", false)
194
- .classed("fa-plus-square-o", true)
195
- ;
196
- break;
197
- }
198
- context.refreshExpandIcon();
199
- })
200
- ;
201
-
202
- const sortIcon = th.append("i")
203
- .attr("class", "sortIcon fa")
204
- .on("click", function () {
205
- context.refreshSortIcon(sortIcon, true);
206
- })
207
- ;
208
-
209
- th.append("i")
210
- .attr("class", "hideParamsIcon fa")
211
- .on("click", function () {
212
- context.hideNonWidgets(!context.hideNonWidgets()).render();
213
- })
214
- ;
215
- }
216
-
217
- updateHeader(th) {
218
- const widget: any = this.widget();
219
- let spanText = "";
220
- if (widget) {
221
- if (widget.label) {
222
- spanText += widget.label();
223
- }
224
- if (widget.classID) {
225
- if (spanText) {
226
- spanText += " - ";
227
- }
228
- spanText += widget.classID();
229
- }
230
- }
231
- th.select("span")
232
- .text(spanText)
233
- ;
234
- this.refreshExpandIcon();
235
- this.refreshSortIcon(th.select(".sortIcon"));
236
- this.refreshHideParamsIcon(th.select(".hideParamsIcon"));
237
- }
238
-
239
- peInputCount() {
240
- return this.element().selectAll(`.table-${this.depth()} > tbody > tr > .headerRow > .peInput > div`).size();
241
- }
242
-
243
- peInputCollapsedCount() {
244
- return this.element().selectAll(`.table-${this.depth()} > tbody > tr > .headerRow > .peInput > div.property-table-collapsed`).size();
245
- }
246
-
247
- peInputIcon(): "fa-caret-down" | "fa-caret-up" | "fa-caret-right" {
248
- const collapsed = this.peInputCollapsedCount();
249
- if (collapsed === 0) {
250
- return "fa-caret-down";
251
- } else if (collapsed === this.peInputCount()) {
252
- return "fa-caret-up";
253
- }
254
- return "fa-caret-right";
255
- }
256
-
257
- refreshExpandIcon() {
258
- const newIcon = this.peInputIcon();
259
- this.element().select(`.table-${this.depth()} > thead > tr > th > .expandIcon`)
260
- .classed("fa-caret-up", false)
261
- .classed("fa-caret-right", false)
262
- .classed("fa-caret-down", false)
263
- .classed(newIcon, true)
264
- ;
265
- }
266
-
267
- refreshSortIcon(sortIcon, increment = false) {
268
- const sort = this.sorting();
269
- const types = this.sorting_options();
270
- const icons = this.__meta_sorting.ext.icons;
271
- if (increment) {
272
- sortIcon.classed(icons[types.indexOf(sort)], false);
273
- this.sorting(types[(types.indexOf(sort) + 1) % types.length]).render();
274
- } else {
275
- sortIcon
276
- .classed(icons[(types.indexOf(sort)) % types.length], true)
277
- .attr("title", sort)
278
- ;
279
- }
280
- }
281
-
282
- refreshHideParamsIcon(hideParamsIcon) {
283
- hideParamsIcon
284
- .classed("fa-eye", !this.hideNonWidgets())
285
- .classed("fa-eye-slash", this.hideNonWidgets())
286
- ;
287
- }
288
-
289
- gatherDataTree(widget) {
290
- if (!widget) return null;
291
- const retVal = {
292
- label: widget.id() + " (" + widget.classID() + ")",
293
- children: []
294
- };
295
- const arr2 = Persist.discover(widget);
296
- arr2.forEach(function (prop) {
297
- const node = {
298
- label: prop.id,
299
- children: []
300
- };
301
- switch (prop.type) {
302
- case "widget":
303
- node.children.push(this.gatherDataTree(widget[prop.id]()));
304
- break;
305
- case "widgetArray":
306
- case "propertyArray":
307
- const arr = widget[prop.id]();
308
- if (arr) {
309
- arr.forEach(function (item) {
310
- node.children.push(this.gatherDataTree(item));
311
- }, this);
312
- }
313
- break;
314
- default:
315
- }
316
- retVal.children.push(node);
317
- }, this);
318
- return retVal;
319
- }
320
-
321
- getDataTree() {
322
- return this.gatherDataTree(this.widget());
323
- }
324
-
325
- _rowSorting(paramArr) {
326
- if (this.sorting() === "type") {
327
- const typeOrder = ["boolean", "number", "string", "html-color", "array", "object", "widget", "widgetArray", "propertyArray"];
328
- paramArr.sort(function (a, b) {
329
- if (a.type === b.type) {
330
- return a.id < b.id ? -1 : 1;
331
- } else {
332
- return typeOrder.indexOf(a.type) < typeOrder.indexOf(b.type) ? -1 : 1;
333
- }
334
- });
335
- } else if (this.sorting() === "A-Z") {
336
- paramArr.sort(function (a, b) { return a.id < b.id ? -1 : 1; });
337
- } else if (this.sorting() === "Z-A") {
338
- paramArr.sort(function (a, b) { return a.id > b.id ? -1 : 1; });
339
- }
340
- }
341
-
342
- filterInputs(d) {
343
- const discArr = Persist.discover(d);
344
- if ((this.filterTags() || this.excludeTags().length > 0 || this.excludeParams.length > 0) && d instanceof PropertyEditor === false) {
345
- const context = this;
346
- return discArr.filter(function (param, _idx) {
347
- if (d[param.id + "_hidden"] && d[param.id + "_hidden"]()) return false;
348
- for (const excludeParamItem of context.excludeParams()) {
349
- const arr = excludeParamItem.split(".");
350
- let widgetName;
351
- let excludeParam;
352
- if (arr.length > 2) {
353
- widgetName = arr[0];
354
- excludeParam = arr[2];
355
- } else {
356
- widgetName = arr[0];
357
- excludeParam = arr[1];
358
- }
359
- if (d.class().indexOf(widgetName) !== -1) {
360
- if (param.id === excludeParam) {
361
- return false;
362
- }
363
- return true;
364
- }
365
- }
366
- if (context.excludeTags().length > 0 && param.ext && param.ext.tags && param.ext.tags.some(function (item) { return (context.excludeTags().indexOf(item) > -1); })) {
367
- return false;
368
- }
369
- if ((context.filterTags() && param.ext && param.ext.tags && param.ext.tags.indexOf(context.filterTags()) !== -1) || !context.filterTags()) {
370
- return true;
371
- }
372
- return false;
373
- });
374
- }
375
- return discArr;
376
- }
377
-
378
- renderInputs(element, d) {
379
- const context = this;
380
- let discArr = [];
381
- const showFields = !this.show_settings() && this.showFields();
382
- if (d) {
383
- discArr = this.filterInputs(d).filter(function (prop) { return prop.id !== "fields" ? true : showFields; });
384
- if (!this.show_settings() && this.showData() && d.data) {
385
- discArr.push({ id: "data", type: "array" });
386
- }
387
- if (this.hideNonWidgets()) {
388
- discArr = discArr.filter(function (n) {
389
- return hasProperties(n.type);
390
- });
391
- }
392
- this._rowSorting(discArr);
393
- }
394
-
395
- const rows = element.selectAll("tr.prop" + this.id()).data(discArr, function (d2) { return d2.id; });
396
- rows.enter().append("tr")
397
- .attr("class", "property-wrapper prop" + this.id())
398
- .each(function (param) {
399
- const tr = d3Select(this);
400
- if (hasProperties(param.type)) {
401
- tr.classed("property-widget-wrapper", true);
402
- tr.append("td")
403
- .attr("colspan", "2")
404
- ;
405
- } else {
406
- tr.classed("property-input-wrapper", true);
407
- tr.append("td")
408
- .classed("property-label", true)
409
- .text(param.id)
410
- ;
411
- const inputCell = tr.append("td")
412
- .classed("property-input-cell", true)
413
- ;
414
- context.enterInputs(d, inputCell, param);
415
- }
416
- }).merge(rows)
417
- .each(function (param) {
418
- const tr = d3Select(this);
419
- tr.classed("disabled", d[param.id + "_disabled"] && d[param.id + "_disabled"]());
420
- tr.classed("invalid", d[param.id + "_valid"] && !d[param.id + "_valid"]());
421
- tr.attr("title", param.description);
422
- if (hasProperties(param.type)) {
423
- context.updateWidgetRow(d, tr.select("td"), param);
424
- } else {
425
- context.updateInputs(d, param);
426
- }
427
- });
428
- rows.exit().each(function (param) {
429
- const tr = d3Select(this);
430
- if (hasProperties(param.type)) {
431
- context.updateWidgetRow(d, tr.select("td"), null);
432
- }
433
- }).remove();
434
- rows.order();
435
- }
436
-
437
- updateWidgetRow(widget: PropertyExt, element, param) {
438
- let tmpWidget = [];
439
- if (widget && param) {
440
- tmpWidget = widget[param.id]() || [];
441
- }
442
- let widgetArr = tmpWidget instanceof Array ? tmpWidget : [tmpWidget];
443
- if (param && param.ext && param.ext.autoExpand) {
444
- // remove empties and ensure last row is an empty ---
445
- let lastModified = true;
446
- const noEmpties = widgetArr.filter(function (row, idx) {
447
- lastModified = row.valid();
448
- row._owner = widget;
449
- return lastModified || idx === widgetArr.length - 1;
450
- }, this);
451
- const widgetDisabled = widget[param.id + "_disabled"] && widget[param.id + "_disabled"]();
452
- let changed = !!(widgetArr.length - noEmpties.length);
453
- if (lastModified && !widgetDisabled) {
454
- changed = true;
455
- const autoExpandWidget = new param.ext.autoExpand()
456
- .owner(widget)
457
- ;
458
- // autoExpandWidget.monitor((id, newVal, oldVal, source) => {
459
- // widget.broadcast(param.id, newVal, oldVal, source);
460
- // });
461
- noEmpties.push(autoExpandWidget);
462
- }
463
- if (changed) {
464
- widget[param.id](noEmpties);
465
- widgetArr = noEmpties;
466
- }
467
- }
468
-
469
- const context = this;
470
- element.classed("headerRow", true);
471
- const peInput = element.selectAll(`div.peInput-${this.depth()}`).data(widgetArr, function (d) { return d.id(); });
472
- peInput.enter().append("div")
473
- .attr("class", `peInput peInput-${this.depth()}`)
474
- .each(function (w) {
475
- const peInputElement = d3Select(this);
476
-
477
- // Header ---
478
- peInputElement.append("span");
479
- peInputElement.append("i")
480
- .attr("class", "fa")
481
- .on("click", function (d) {
482
- const clickTarget = peInputElement.select("div");
483
- clickTarget
484
- .classed("property-table-collapsed", !clickTarget.classed("property-table-collapsed"))
485
- ;
486
- d3Select(this)
487
- .classed("fa-minus-square-o", !clickTarget.classed("property-table-collapsed"))
488
- .classed("fa-plus-square-o", clickTarget.classed("property-table-collapsed"))
489
- ;
490
- context.refreshExpandIcon();
491
- })
492
- ;
493
-
494
- // Body ---
495
- const peDiv = peInputElement.append("div")
496
- // .attr("class", `property- input - cell propEditor-${context.depth() }`)
497
- ;
498
- context._childPE.set(this, new PropertyEditor().label(param.id).target(peDiv.node() as HTMLElement));
499
- })
500
- .merge(peInput)
501
- .each(function (w) {
502
- const peInputElement = d3Select(this);
503
- const clickTarget = peInputElement.select("div");
504
-
505
- // Header ---
506
- d3Select(this).select("span")
507
- .text(`${param.id}`)
508
- ;
509
-
510
- d3Select(this).select("i")
511
- .classed("fa-minus-square-o", !clickTarget.classed("property-table-collapsed"))
512
- .classed("fa-plus-square-o", clickTarget.classed("property-table-collapsed"))
513
- ;
514
-
515
- // Body ---
516
- context._childPE.get(this)
517
- .parentPropertyEditor(context)
518
- .showFields(context.showFields())
519
- .showData(context.showData())
520
- .sorting(context.sorting())
521
- .filterTags(context.filterTags())
522
- .excludeTags(context.excludeTags())
523
- .excludeParams(context.excludeParams())
524
- .hideNonWidgets(context.hideNonWidgets() && w._class.indexOf("layout_") >= 0)
525
- .widget(w)
526
- .render()
527
- ;
528
- })
529
- ;
530
- peInput.exit()
531
- .each(function (w) {
532
- context._childPE.get(this)
533
- .widget(null)
534
- .render()
535
- .target(null)
536
- ;
537
- context._childPE.remove(this);
538
- })
539
- .remove()
540
- ;
541
- }
542
-
543
- setProperty(widget, id, value) {
544
- // With PropertyExt not all "widgets" have a render, if not use top most render...
545
- let topWidget: Widget;
546
- let topPropEditor: Widget;
547
- let propEditor: PropertyEditor = this;
548
- let oldValue;
549
- while (propEditor && widget) {
550
- if (propEditor === this) {
551
- oldValue = widget[id]();
552
- widget[id](value);
553
- }
554
- if (propEditor) {
555
- topPropEditor = propEditor;
556
- const w: PropertyExt = propEditor.widget();
557
- if (w instanceof Widget) {
558
- topWidget = w;
559
- }
560
- }
561
- propEditor = propEditor.parentPropertyEditor();
562
- }
563
- if (topWidget) {
564
- topWidget.render();
565
- }
566
- if (topPropEditor) {
567
- topPropEditor.broadcast(id, value, oldValue, widget);
568
- }
569
- }
570
-
571
- enterInputs(widget, cell, param) {
572
- cell.classed(param.type + "-cell", true);
573
- const context = this;
574
- if (typeof (param.ext.editor_input) === "function") {
575
- param.ext.editor_input(this, widget, cell, param);
576
- }
577
- switch (param.type) {
578
- case "boolean":
579
- cell.append("input")
580
- .attr("id", this.id() + "_" + param.id)
581
- .classed("property-input", true)
582
- .attr("type", "checkbox")
583
- .on("change", function () {
584
- context.setProperty(widget, param.id, this.checked);
585
- })
586
- ;
587
- break;
588
- case "set":
589
- cell.append("select")
590
- .attr("id", this.id() + "_" + param.id)
591
- .classed("property-input", true)
592
- .on("change", function () {
593
- context.setProperty(widget, param.id, this.value);
594
- })
595
- ;
596
- break;
597
- case "array":
598
- case "object":
599
- cell.append("textarea")
600
- .attr("id", this.id() + "_" + param.id)
601
- .classed("property-input", true)
602
- .attr("autocomplete", "off")
603
- .attr("autocorrect", "off")
604
- .attr("autocapitalize", "off")
605
- .attr("spellcheck", "false")
606
- .on("change", function () {
607
- let value;
608
- try {
609
- value = JSON.parse(this.value);
610
- } catch (e) {
611
- value = this.value;
612
- }
613
- context.setProperty(widget, param.id, value);
614
- })
615
- ;
616
- break;
617
- default:
618
- if (param.ext && param.ext.range) {
619
- cell.append("span")
620
- .classed("property-input-span", true)
621
- .attr("id", this.id() + "_" + param.id + "_currentVal")
622
- .text(param.defaultValue)
623
- ;
624
- cell.append("input")
625
- .attr("type", "range")
626
- .attr("step", param.ext.range.step)
627
- .attr("min", param.ext.range.min)
628
- .attr("max", param.ext.range.max)
629
- .attr("id", this.id() + "_" + param.id)
630
- .classed("property-input", true)
631
- .on("input", function () {
632
- context.setProperty(widget, param.id, this.value);
633
- d3Select("#" + this.id + "_currentVal").text("Current Value: " + this.value);
634
- })
635
- .on("change", function () {
636
- context.setProperty(widget, param.id, this.value);
637
- d3Select("#" + this.id + "_currentVal").text("Current Value: " + this.value);
638
- })
639
- ;
640
- } else {
641
- cell.append(param.ext && param.ext.multiline ? "textarea" : "input")
642
- .attr("id", this.id() + "_" + param.id)
643
- .classed("property-input", true)
644
- .attr("autocomplete", "off")
645
- .attr("autocorrect", "off")
646
- .attr("autocapitalize", "off")
647
- .attr("spellcheck", "false")
648
- .on("change", function () {
649
- context.setProperty(widget, param.id, this.value);
650
- })
651
- ;
652
- if (param.type === "html-color" && !Platform.isIE) {
653
- cell.append("input")
654
- .attr("id", this.id() + "_" + param.id + "_2")
655
- .classed("property-input", true)
656
- .attr("type", "color")
657
- .on("change", function () {
658
- context.setProperty(widget, param.id, this.value);
659
- })
660
- ;
661
- }
662
- }
663
- break;
664
- }
665
- }
666
-
667
- updateInputs(widget, param) {
668
- const element = d3SelectAll("#" + this.id() + "_" + param.id + ", #" + this.id() + "_" + param.id + "_2");
669
- const val = widget ? widget[param.id]() : "";
670
- element.property("disabled", widget[param.id + "_disabled"] && widget[param.id + "_disabled"]());
671
- element.property("invalid", widget[param.id + "_valid"] && !widget[param.id + "_valid"]());
672
- switch (param.type) {
673
- case "boolean":
674
- element.property("checked", val);
675
- break;
676
- case "set":
677
- const options = element.selectAll("option").data<string | { value: string, text: string }>(widget[param.id + "_options"]());
678
- options.enter().append("option")
679
- .merge(options as any)
680
- .attr("value", (d: any) => (d && d.value !== undefined) ? d.value : d)
681
- .text((d: any) => (d && d.text !== undefined) ? d.text : d)
682
- ;
683
- options.exit().remove();
684
- element.property("value", val);
685
- break;
686
- case "array":
687
- case "object":
688
- element.property("value", JSON.stringify(val, function replacer(_key, value) {
689
- if (value instanceof Widget) {
690
- return Persist.serialize(value);
691
- }
692
- return value;
693
- }, " "));
694
- break;
695
- default:
696
- if (param.ext && param.ext.range) {
697
- d3Select("#" + this.id() + "_" + param.id + "_currentVal").text("Current Value: " + val);
698
- }
699
- element.property("value", val && val.length && val.length > 100000 ? "...too big to display..." : val);
700
- break;
701
- }
702
- }
703
- }
704
- PropertyEditor.prototype._class += " other_PropertyEditor";
705
-
706
- export interface PropertyEditor {
707
- showFields(): boolean;
708
- showFields(_: boolean): this;
709
- showData(): boolean;
710
- showData(_: boolean): this;
711
-
712
- sorting(): string;
713
- sorting(_: string): this;
714
- sorting_options(): string[];
715
- sorting_options(_: string[]): this;
716
-
717
- hideNonWidgets(): boolean;
718
- hideNonWidgets(_: boolean): this;
719
-
720
- label(): string;
721
- label(_: string): this;
722
- filterTags(): string;
723
- filterTags(_: string): this;
724
- excludeTags(): string[];
725
- excludeTags(_: string[]): this;
726
- excludeParams(): string[];
727
- excludeParams(_: string[]): this;
728
-
729
- widget(): PropertyExt;
730
- widget(_: PropertyExt): this;
731
- }
732
-
733
- PropertyEditor.prototype.publish("showFields", false, "boolean", "If true, widget.fields() will display as if it was a publish parameter.", null, { tags: ["Basic"] });
734
- PropertyEditor.prototype.publish("showData", false, "boolean", "If true, widget.data() will display as if it was a publish parameter.", null, { tags: ["Basic"] });
735
-
736
- PropertyEditor.prototype.publish("sorting", "none", "set", "Specify the sorting type", ["none", "A-Z", "Z-A", "type"], { tags: ["Basic"], icons: ["fa-sort", "fa-sort-alpha-asc", "fa-sort-alpha-desc", "fa-sort-amount-asc"] });
737
-
738
- PropertyEditor.prototype.publish("hideNonWidgets", false, "boolean", "Hides non-widget params (at this tier only)", null, { tags: ["Basic"] });
739
-
740
- PropertyEditor.prototype.publish("label", "", "string", "Label to display in header of property editor table", null, { tags: ["Basic"] });
741
- PropertyEditor.prototype.publish("filterTags", "", "set", "Only show Publish Params of this type", ["Basic", "Intermediate", "Advance", ""], {});
742
- PropertyEditor.prototype.publish("excludeTags", ["Private"], "array", "Exclude this array of tags", null, {});
743
- PropertyEditor.prototype.publish("excludeParams", [], "array", "Exclude this array of params (widget.param)", null, {});
744
-
745
- PropertyEditor.prototype.publish("widget", null, "widget", "Widget", null, { tags: ["Basic"], render: false });
746
-
747
- const _widgetOrig = PropertyEditor.prototype.widget;
748
- (PropertyEditor.prototype as any).widget = function (_?: Widget): Widget | PropertyEditor {
749
- if (arguments.length && _widgetOrig.call(this) === _) return this;
750
- const retVal = _widgetOrig.apply(this, arguments);
751
- if (arguments.length) {
752
- this.watchWidget(_);
753
- if (_ instanceof Grid) {
754
- const context = this;
755
- _.postSelectionChange = function () {
756
- context._selectedItems = _._selectionBag.get().map(function (item) { return item.widget; });
757
- context.lazyRender();
758
- };
759
- }
760
- }
761
- return retVal;
762
- };
1
+ import { HTMLWidget, Platform, PropertyExt, Widget } from "@hpcc-js/common";
2
+ import { Grid } from "@hpcc-js/layout";
3
+ import { local as d3Local, select as d3Select, selectAll as d3SelectAll } from "d3-selection";
4
+ import * as Persist from "./Persist.ts";
5
+
6
+ import "../src/PropertyEditor.css";
7
+
8
+ function hasProperties(type) {
9
+ switch (type) {
10
+ case "widget":
11
+ case "widgetArray":
12
+ case "propertyArray":
13
+ return true;
14
+ default:
15
+ }
16
+ return false;
17
+ }
18
+
19
+ export class PropertyEditor extends HTMLWidget {
20
+ _widgetOrig;
21
+ _parentPropertyEditor;
22
+ _show_settings: boolean;
23
+ _selectedItems;
24
+ __meta_sorting;
25
+ _watch;
26
+ private _childPE = d3Local<PropertyEditor>();
27
+
28
+ constructor() {
29
+ super();
30
+ this._parentPropertyEditor = null;
31
+
32
+ this._tag = "div";
33
+ this._show_settings = false;
34
+ }
35
+
36
+ parentPropertyEditor(_?: PropertyEditor): PropertyEditor {
37
+ if (!arguments.length) return this._parentPropertyEditor;
38
+ this._parentPropertyEditor = _;
39
+ return this;
40
+ }
41
+
42
+ depth(): number {
43
+ let retVal = 0;
44
+ let parent = this.parentPropertyEditor();
45
+ while (parent) {
46
+ ++retVal;
47
+ parent = parent.parentPropertyEditor();
48
+ }
49
+ return retVal;
50
+ }
51
+
52
+ _show_header = true;
53
+ show_header(): boolean;
54
+ show_header(_: boolean): PropertyEditor;
55
+ show_header(_?: boolean): boolean | PropertyEditor {
56
+ if (!arguments.length) {
57
+ return this._show_header;
58
+ }
59
+ this._show_header = _;
60
+ return this;
61
+ }
62
+
63
+ show_settings(): boolean;
64
+ show_settings(_: boolean): PropertyEditor;
65
+ show_settings(_?: boolean): boolean | PropertyEditor {
66
+ if (!arguments.length) {
67
+ return this._show_settings;
68
+ }
69
+ this._show_settings = _;
70
+ return this;
71
+ }
72
+
73
+ rootWidgets() {
74
+ if (this._selectedItems && this._selectedItems.length) {
75
+ return this._selectedItems;
76
+ }
77
+ return this.show_settings() ? [this] : this.widget() ? [this.widget()] : [];
78
+ }
79
+
80
+ update(domNode, element) {
81
+ super.update(domNode, element);
82
+
83
+ const context = this;
84
+
85
+ const rootWidgets = this.rootWidgets().filter(function (w) {
86
+ if (w._owningWidget && w._owningWidget.excludeObjs instanceof Array) {
87
+ if (w._owningWidget.excludeObjs.indexOf(w.classID()) !== -1) {
88
+ return false;
89
+ }
90
+ }
91
+ return true;
92
+ });
93
+
94
+ const table = element.selectAll(`table.property-table.table-${this.depth()}`).data(rootWidgets, function (d) {
95
+ // We reuse the existing DOM Nodes and this node _might_ have been a regular Input previously ---
96
+ if (typeof d.id !== "function") {
97
+ return `meta-${d.id}`;
98
+ }
99
+ return d.id();
100
+ });
101
+ table.enter().append("table")
102
+ .attr("class", `property-table table-${this.depth()}`)
103
+ .each(function () {
104
+ const tableElement = d3Select(this);
105
+
106
+ // Header ---
107
+ if (context._show_header && context.parentPropertyEditor() === null) {
108
+ tableElement.append("thead").append("tr").append("th")// .datum(tableElement)
109
+ .attr("colspan", "2")
110
+ .each(function () {
111
+ context.enterHeader(d3Select(this));
112
+ })
113
+ ;
114
+ }
115
+
116
+ // Body ---
117
+ tableElement.append("tbody");
118
+ })
119
+ .merge(table)
120
+ .each(function (tableData) {
121
+ const tableElement = d3Select(this);
122
+
123
+ // Header ---
124
+ if (context._show_header && context.parentPropertyEditor() === null) {
125
+ context.updateHeader(tableElement.select("thead > tr > th"));
126
+ }
127
+
128
+ // Body ---
129
+ context.renderInputs(tableElement.select("tbody"), tableData);
130
+ })
131
+ ;
132
+ table.exit()
133
+ .each(function () {
134
+ context.renderInputs(element.select("tbody"), null);
135
+ })
136
+ .remove()
137
+ ;
138
+ }
139
+
140
+ exit(domNode, element) {
141
+ super.exit(domNode, element);
142
+ this.watchWidget(null);
143
+ }
144
+
145
+ private watchDepth = 0;
146
+ watchWidget(widget) {
147
+ if (this._watch) {
148
+ if ((window as any).__hpcc_debug) {
149
+ --this.watchDepth;
150
+ console.info("watchDepth: " + this.watchDepth);
151
+ }
152
+ this._watch.remove();
153
+ delete this._watch;
154
+ }
155
+ if (widget) {
156
+ const context = this;
157
+ this._watch = widget.monitor(function (_paramId, newVal, oldVal) {
158
+ if (oldVal !== newVal) {
159
+ const propEditor = context.parentPropertyEditor() || context;
160
+ propEditor.lazyRender();
161
+ }
162
+ });
163
+ if ((window as any).__hpcc_debug) {
164
+ ++this.watchDepth;
165
+ console.info("watchDepth: " + this.watchDepth);
166
+ }
167
+ }
168
+ }
169
+
170
+ enterHeader(th) {
171
+ const context = this;
172
+
173
+ th.append("span");
174
+ th.append("i")
175
+ .attr("class", "expandIcon fa")
176
+ .on("click", function () {
177
+ switch (context.peInputIcon()) {
178
+ case "fa-caret-up":
179
+ case "fa-caret-right":
180
+ context.element().selectAll(`.table-${context.depth()} > tbody > tr > .headerRow > .peInput > .property-table-collapsed`)
181
+ .classed("property-table-collapsed", false)
182
+ ;
183
+ context.element().selectAll(`.table-${context.depth()} > tbody > tr > .headerRow > .peInput > i`)
184
+ .classed("fa-minus-square-o", true)
185
+ .classed("fa-plus-square-o", false)
186
+ ;
187
+ break;
188
+ case "fa-caret-down":
189
+ context.element().selectAll(`.table-${context.depth()} > tbody > tr > .headerRow > .peInput > div`)
190
+ .classed("property-table-collapsed", true)
191
+ ;
192
+ context.element().selectAll(`.table-${context.depth()} > tbody > tr > .headerRow > .peInput > i`)
193
+ .classed("fa-minus-square-o", false)
194
+ .classed("fa-plus-square-o", true)
195
+ ;
196
+ break;
197
+ }
198
+ context.refreshExpandIcon();
199
+ })
200
+ ;
201
+
202
+ const sortIcon = th.append("i")
203
+ .attr("class", "sortIcon fa")
204
+ .on("click", function () {
205
+ context.refreshSortIcon(sortIcon, true);
206
+ })
207
+ ;
208
+
209
+ th.append("i")
210
+ .attr("class", "hideParamsIcon fa")
211
+ .on("click", function () {
212
+ context.hideNonWidgets(!context.hideNonWidgets()).render();
213
+ })
214
+ ;
215
+ }
216
+
217
+ updateHeader(th) {
218
+ const widget: any = this.widget();
219
+ let spanText = "";
220
+ if (widget) {
221
+ if (widget.label) {
222
+ spanText += widget.label();
223
+ }
224
+ if (widget.classID) {
225
+ if (spanText) {
226
+ spanText += " - ";
227
+ }
228
+ spanText += widget.classID();
229
+ }
230
+ }
231
+ th.select("span")
232
+ .text(spanText)
233
+ ;
234
+ this.refreshExpandIcon();
235
+ this.refreshSortIcon(th.select(".sortIcon"));
236
+ this.refreshHideParamsIcon(th.select(".hideParamsIcon"));
237
+ }
238
+
239
+ peInputCount() {
240
+ return this.element().selectAll(`.table-${this.depth()} > tbody > tr > .headerRow > .peInput > div`).size();
241
+ }
242
+
243
+ peInputCollapsedCount() {
244
+ return this.element().selectAll(`.table-${this.depth()} > tbody > tr > .headerRow > .peInput > div.property-table-collapsed`).size();
245
+ }
246
+
247
+ peInputIcon(): "fa-caret-down" | "fa-caret-up" | "fa-caret-right" {
248
+ const collapsed = this.peInputCollapsedCount();
249
+ if (collapsed === 0) {
250
+ return "fa-caret-down";
251
+ } else if (collapsed === this.peInputCount()) {
252
+ return "fa-caret-up";
253
+ }
254
+ return "fa-caret-right";
255
+ }
256
+
257
+ refreshExpandIcon() {
258
+ const newIcon = this.peInputIcon();
259
+ this.element().select(`.table-${this.depth()} > thead > tr > th > .expandIcon`)
260
+ .classed("fa-caret-up", false)
261
+ .classed("fa-caret-right", false)
262
+ .classed("fa-caret-down", false)
263
+ .classed(newIcon, true)
264
+ ;
265
+ }
266
+
267
+ refreshSortIcon(sortIcon, increment = false) {
268
+ const sort = this.sorting();
269
+ const types = this.sorting_options();
270
+ const icons = this.__meta_sorting.ext.icons;
271
+ if (increment) {
272
+ sortIcon.classed(icons[types.indexOf(sort)], false);
273
+ this.sorting(types[(types.indexOf(sort) + 1) % types.length]).render();
274
+ } else {
275
+ sortIcon
276
+ .classed(icons[(types.indexOf(sort)) % types.length], true)
277
+ .attr("title", sort)
278
+ ;
279
+ }
280
+ }
281
+
282
+ refreshHideParamsIcon(hideParamsIcon) {
283
+ hideParamsIcon
284
+ .classed("fa-eye", !this.hideNonWidgets())
285
+ .classed("fa-eye-slash", this.hideNonWidgets())
286
+ ;
287
+ }
288
+
289
+ gatherDataTree(widget) {
290
+ if (!widget) return null;
291
+ const retVal = {
292
+ label: widget.id() + " (" + widget.classID() + ")",
293
+ children: []
294
+ };
295
+ const arr2 = Persist.discover(widget);
296
+ arr2.forEach(function (prop) {
297
+ const node = {
298
+ label: prop.id,
299
+ children: []
300
+ };
301
+ switch (prop.type) {
302
+ case "widget":
303
+ node.children.push(this.gatherDataTree(widget[prop.id]()));
304
+ break;
305
+ case "widgetArray":
306
+ case "propertyArray":
307
+ const arr = widget[prop.id]();
308
+ if (arr) {
309
+ arr.forEach(function (item) {
310
+ node.children.push(this.gatherDataTree(item));
311
+ }, this);
312
+ }
313
+ break;
314
+ default:
315
+ }
316
+ retVal.children.push(node);
317
+ }, this);
318
+ return retVal;
319
+ }
320
+
321
+ getDataTree() {
322
+ return this.gatherDataTree(this.widget());
323
+ }
324
+
325
+ _rowSorting(paramArr) {
326
+ if (this.sorting() === "type") {
327
+ const typeOrder = ["boolean", "number", "string", "html-color", "array", "object", "widget", "widgetArray", "propertyArray"];
328
+ paramArr.sort(function (a, b) {
329
+ if (a.type === b.type) {
330
+ return a.id < b.id ? -1 : 1;
331
+ } else {
332
+ return typeOrder.indexOf(a.type) < typeOrder.indexOf(b.type) ? -1 : 1;
333
+ }
334
+ });
335
+ } else if (this.sorting() === "A-Z") {
336
+ paramArr.sort(function (a, b) { return a.id < b.id ? -1 : 1; });
337
+ } else if (this.sorting() === "Z-A") {
338
+ paramArr.sort(function (a, b) { return a.id > b.id ? -1 : 1; });
339
+ }
340
+ }
341
+
342
+ filterInputs(d) {
343
+ const discArr = Persist.discover(d);
344
+ if ((this.filterTags() || this.excludeTags().length > 0 || this.excludeParams.length > 0) && d instanceof PropertyEditor === false) {
345
+ const context = this;
346
+ return discArr.filter(function (param, _idx) {
347
+ if (d[param.id + "_hidden"] && d[param.id + "_hidden"]()) return false;
348
+ for (const excludeParamItem of context.excludeParams()) {
349
+ const arr = excludeParamItem.split(".");
350
+ let widgetName;
351
+ let excludeParam;
352
+ if (arr.length > 2) {
353
+ widgetName = arr[0];
354
+ excludeParam = arr[2];
355
+ } else {
356
+ widgetName = arr[0];
357
+ excludeParam = arr[1];
358
+ }
359
+ if (d.class().indexOf(widgetName) !== -1) {
360
+ if (param.id === excludeParam) {
361
+ return false;
362
+ }
363
+ return true;
364
+ }
365
+ }
366
+ if (context.excludeTags().length > 0 && param.ext && param.ext.tags && param.ext.tags.some(function (item) { return (context.excludeTags().indexOf(item) > -1); })) {
367
+ return false;
368
+ }
369
+ if ((context.filterTags() && param.ext && param.ext.tags && param.ext.tags.indexOf(context.filterTags()) !== -1) || !context.filterTags()) {
370
+ return true;
371
+ }
372
+ return false;
373
+ });
374
+ }
375
+ return discArr;
376
+ }
377
+
378
+ renderInputs(element, d) {
379
+ const context = this;
380
+ let discArr = [];
381
+ const showFields = !this.show_settings() && this.showFields();
382
+ if (d) {
383
+ discArr = this.filterInputs(d).filter(function (prop) { return prop.id !== "fields" ? true : showFields; });
384
+ if (!this.show_settings() && this.showData() && d.data) {
385
+ discArr.push({ id: "data", type: "array" });
386
+ }
387
+ if (this.hideNonWidgets()) {
388
+ discArr = discArr.filter(function (n) {
389
+ return hasProperties(n.type);
390
+ });
391
+ }
392
+ this._rowSorting(discArr);
393
+ }
394
+
395
+ const rows = element.selectAll("tr.prop" + this.id()).data(discArr, function (d2) { return d2.id; });
396
+ rows.enter().append("tr")
397
+ .attr("class", "property-wrapper prop" + this.id())
398
+ .each(function (param) {
399
+ const tr = d3Select(this);
400
+ if (hasProperties(param.type)) {
401
+ tr.classed("property-widget-wrapper", true);
402
+ tr.append("td")
403
+ .attr("colspan", "2")
404
+ ;
405
+ } else {
406
+ tr.classed("property-input-wrapper", true);
407
+ tr.append("td")
408
+ .classed("property-label", true)
409
+ .text(param.id)
410
+ ;
411
+ const inputCell = tr.append("td")
412
+ .classed("property-input-cell", true)
413
+ ;
414
+ context.enterInputs(d, inputCell, param);
415
+ }
416
+ }).merge(rows)
417
+ .each(function (param) {
418
+ const tr = d3Select(this);
419
+ tr.classed("disabled", d[param.id + "_disabled"] && d[param.id + "_disabled"]());
420
+ tr.classed("invalid", d[param.id + "_valid"] && !d[param.id + "_valid"]());
421
+ tr.attr("title", param.description);
422
+ if (hasProperties(param.type)) {
423
+ context.updateWidgetRow(d, tr.select("td"), param);
424
+ } else {
425
+ context.updateInputs(d, param);
426
+ }
427
+ });
428
+ rows.exit().each(function (param) {
429
+ const tr = d3Select(this);
430
+ if (hasProperties(param.type)) {
431
+ context.updateWidgetRow(d, tr.select("td"), null);
432
+ }
433
+ }).remove();
434
+ rows.order();
435
+ }
436
+
437
+ updateWidgetRow(widget: PropertyExt, element, param) {
438
+ let tmpWidget = [];
439
+ if (widget && param) {
440
+ tmpWidget = widget[param.id]() || [];
441
+ }
442
+ let widgetArr = tmpWidget instanceof Array ? tmpWidget : [tmpWidget];
443
+ if (param && param.ext && param.ext.autoExpand) {
444
+ // remove empties and ensure last row is an empty ---
445
+ let lastModified = true;
446
+ const noEmpties = widgetArr.filter(function (row, idx) {
447
+ lastModified = row.valid();
448
+ row._owner = widget;
449
+ return lastModified || idx === widgetArr.length - 1;
450
+ }, this);
451
+ const widgetDisabled = widget[param.id + "_disabled"] && widget[param.id + "_disabled"]();
452
+ let changed = !!(widgetArr.length - noEmpties.length);
453
+ if (lastModified && !widgetDisabled) {
454
+ changed = true;
455
+ const autoExpandWidget = new param.ext.autoExpand()
456
+ .owner(widget)
457
+ ;
458
+ // autoExpandWidget.monitor((id, newVal, oldVal, source) => {
459
+ // widget.broadcast(param.id, newVal, oldVal, source);
460
+ // });
461
+ noEmpties.push(autoExpandWidget);
462
+ }
463
+ if (changed) {
464
+ widget[param.id](noEmpties);
465
+ widgetArr = noEmpties;
466
+ }
467
+ }
468
+
469
+ const context = this;
470
+ element.classed("headerRow", true);
471
+ const peInput = element.selectAll(`div.peInput-${this.depth()}`).data(widgetArr, function (d) { return d.id(); });
472
+ peInput.enter().append("div")
473
+ .attr("class", `peInput peInput-${this.depth()}`)
474
+ .each(function (w) {
475
+ const peInputElement = d3Select(this);
476
+
477
+ // Header ---
478
+ peInputElement.append("span");
479
+ peInputElement.append("i")
480
+ .attr("class", "fa")
481
+ .on("click", function (d) {
482
+ const clickTarget = peInputElement.select("div");
483
+ clickTarget
484
+ .classed("property-table-collapsed", !clickTarget.classed("property-table-collapsed"))
485
+ ;
486
+ d3Select(this)
487
+ .classed("fa-minus-square-o", !clickTarget.classed("property-table-collapsed"))
488
+ .classed("fa-plus-square-o", clickTarget.classed("property-table-collapsed"))
489
+ ;
490
+ context.refreshExpandIcon();
491
+ })
492
+ ;
493
+
494
+ // Body ---
495
+ const peDiv = peInputElement.append("div")
496
+ // .attr("class", `property- input - cell propEditor-${context.depth() }`)
497
+ ;
498
+ context._childPE.set(this, new PropertyEditor().label(param.id).target(peDiv.node() as HTMLElement));
499
+ })
500
+ .merge(peInput)
501
+ .each(function (w) {
502
+ const peInputElement = d3Select(this);
503
+ const clickTarget = peInputElement.select("div");
504
+
505
+ // Header ---
506
+ d3Select(this).select("span")
507
+ .text(`${param.id}`)
508
+ ;
509
+
510
+ d3Select(this).select("i")
511
+ .classed("fa-minus-square-o", !clickTarget.classed("property-table-collapsed"))
512
+ .classed("fa-plus-square-o", clickTarget.classed("property-table-collapsed"))
513
+ ;
514
+
515
+ // Body ---
516
+ context._childPE.get(this)
517
+ .parentPropertyEditor(context)
518
+ .showFields(context.showFields())
519
+ .showData(context.showData())
520
+ .sorting(context.sorting())
521
+ .filterTags(context.filterTags())
522
+ .excludeTags(context.excludeTags())
523
+ .excludeParams(context.excludeParams())
524
+ .hideNonWidgets(context.hideNonWidgets() && w._class.indexOf("layout_") >= 0)
525
+ .widget(w)
526
+ .render()
527
+ ;
528
+ })
529
+ ;
530
+ peInput.exit()
531
+ .each(function (w) {
532
+ context._childPE.get(this)
533
+ .widget(null)
534
+ .render()
535
+ .target(null)
536
+ ;
537
+ context._childPE.remove(this);
538
+ })
539
+ .remove()
540
+ ;
541
+ }
542
+
543
+ setProperty(widget, id, value) {
544
+ // With PropertyExt not all "widgets" have a render, if not use top most render...
545
+ let topWidget: Widget;
546
+ let topPropEditor: Widget;
547
+ let propEditor: PropertyEditor = this;
548
+ let oldValue;
549
+ while (propEditor && widget) {
550
+ if (propEditor === this) {
551
+ oldValue = widget[id]();
552
+ widget[id](value);
553
+ }
554
+ if (propEditor) {
555
+ topPropEditor = propEditor;
556
+ const w: PropertyExt = propEditor.widget();
557
+ if (w instanceof Widget) {
558
+ topWidget = w;
559
+ }
560
+ }
561
+ propEditor = propEditor.parentPropertyEditor();
562
+ }
563
+ if (topWidget) {
564
+ topWidget.render();
565
+ }
566
+ if (topPropEditor) {
567
+ topPropEditor.broadcast(id, value, oldValue, widget);
568
+ }
569
+ }
570
+
571
+ enterInputs(widget, cell, param) {
572
+ cell.classed(param.type + "-cell", true);
573
+ const context = this;
574
+ if (typeof (param.ext.editor_input) === "function") {
575
+ param.ext.editor_input(this, widget, cell, param);
576
+ }
577
+ switch (param.type) {
578
+ case "boolean":
579
+ cell.append("input")
580
+ .attr("id", this.id() + "_" + param.id)
581
+ .classed("property-input", true)
582
+ .attr("type", "checkbox")
583
+ .on("change", function () {
584
+ context.setProperty(widget, param.id, this.checked);
585
+ })
586
+ ;
587
+ break;
588
+ case "set":
589
+ cell.append("select")
590
+ .attr("id", this.id() + "_" + param.id)
591
+ .classed("property-input", true)
592
+ .on("change", function () {
593
+ context.setProperty(widget, param.id, this.value);
594
+ })
595
+ ;
596
+ break;
597
+ case "array":
598
+ case "object":
599
+ cell.append("textarea")
600
+ .attr("id", this.id() + "_" + param.id)
601
+ .classed("property-input", true)
602
+ .attr("autocomplete", "off")
603
+ .attr("autocorrect", "off")
604
+ .attr("autocapitalize", "off")
605
+ .attr("spellcheck", "false")
606
+ .on("change", function () {
607
+ let value;
608
+ try {
609
+ value = JSON.parse(this.value);
610
+ } catch (e) {
611
+ value = this.value;
612
+ }
613
+ context.setProperty(widget, param.id, value);
614
+ })
615
+ ;
616
+ break;
617
+ default:
618
+ if (param.ext && param.ext.range) {
619
+ cell.append("span")
620
+ .classed("property-input-span", true)
621
+ .attr("id", this.id() + "_" + param.id + "_currentVal")
622
+ .text(param.defaultValue)
623
+ ;
624
+ cell.append("input")
625
+ .attr("type", "range")
626
+ .attr("step", param.ext.range.step)
627
+ .attr("min", param.ext.range.min)
628
+ .attr("max", param.ext.range.max)
629
+ .attr("id", this.id() + "_" + param.id)
630
+ .classed("property-input", true)
631
+ .on("input", function () {
632
+ context.setProperty(widget, param.id, this.value);
633
+ d3Select("#" + this.id + "_currentVal").text("Current Value: " + this.value);
634
+ })
635
+ .on("change", function () {
636
+ context.setProperty(widget, param.id, this.value);
637
+ d3Select("#" + this.id + "_currentVal").text("Current Value: " + this.value);
638
+ })
639
+ ;
640
+ } else {
641
+ cell.append(param.ext && param.ext.multiline ? "textarea" : "input")
642
+ .attr("id", this.id() + "_" + param.id)
643
+ .classed("property-input", true)
644
+ .attr("autocomplete", "off")
645
+ .attr("autocorrect", "off")
646
+ .attr("autocapitalize", "off")
647
+ .attr("spellcheck", "false")
648
+ .on("change", function () {
649
+ context.setProperty(widget, param.id, this.value);
650
+ })
651
+ ;
652
+ if (param.type === "html-color" && !Platform.isIE) {
653
+ cell.append("input")
654
+ .attr("id", this.id() + "_" + param.id + "_2")
655
+ .classed("property-input", true)
656
+ .attr("type", "color")
657
+ .on("change", function () {
658
+ context.setProperty(widget, param.id, this.value);
659
+ })
660
+ ;
661
+ }
662
+ }
663
+ break;
664
+ }
665
+ }
666
+
667
+ updateInputs(widget, param) {
668
+ const element = d3SelectAll("#" + this.id() + "_" + param.id + ", #" + this.id() + "_" + param.id + "_2");
669
+ const val = widget ? widget[param.id]() : "";
670
+ element.property("disabled", widget[param.id + "_disabled"] && widget[param.id + "_disabled"]());
671
+ element.property("invalid", widget[param.id + "_valid"] && !widget[param.id + "_valid"]());
672
+ switch (param.type) {
673
+ case "boolean":
674
+ element.property("checked", val);
675
+ break;
676
+ case "set":
677
+ const options = element.selectAll("option").data<string | { value: string, text: string }>(widget[param.id + "_options"]());
678
+ options.enter().append("option")
679
+ .merge(options as any)
680
+ .attr("value", (d: any) => (d && d.value !== undefined) ? d.value : d)
681
+ .text((d: any) => (d && d.text !== undefined) ? d.text : d)
682
+ ;
683
+ options.exit().remove();
684
+ element.property("value", val);
685
+ break;
686
+ case "array":
687
+ case "object":
688
+ element.property("value", JSON.stringify(val, function replacer(_key, value) {
689
+ if (value instanceof Widget) {
690
+ return Persist.serialize(value);
691
+ }
692
+ return value;
693
+ }, " "));
694
+ break;
695
+ default:
696
+ if (param.ext && param.ext.range) {
697
+ d3Select("#" + this.id() + "_" + param.id + "_currentVal").text("Current Value: " + val);
698
+ }
699
+ element.property("value", val && val.length && val.length > 100000 ? "...too big to display..." : val);
700
+ break;
701
+ }
702
+ }
703
+ }
704
+ PropertyEditor.prototype._class += " other_PropertyEditor";
705
+
706
+ export interface PropertyEditor {
707
+ showFields(): boolean;
708
+ showFields(_: boolean): this;
709
+ showData(): boolean;
710
+ showData(_: boolean): this;
711
+
712
+ sorting(): string;
713
+ sorting(_: string): this;
714
+ sorting_options(): string[];
715
+ sorting_options(_: string[]): this;
716
+
717
+ hideNonWidgets(): boolean;
718
+ hideNonWidgets(_: boolean): this;
719
+
720
+ label(): string;
721
+ label(_: string): this;
722
+ filterTags(): string;
723
+ filterTags(_: string): this;
724
+ excludeTags(): string[];
725
+ excludeTags(_: string[]): this;
726
+ excludeParams(): string[];
727
+ excludeParams(_: string[]): this;
728
+
729
+ widget(): PropertyExt;
730
+ widget(_: PropertyExt): this;
731
+ }
732
+
733
+ PropertyEditor.prototype.publish("showFields", false, "boolean", "If true, widget.fields() will display as if it was a publish parameter.", null, { tags: ["Basic"] });
734
+ PropertyEditor.prototype.publish("showData", false, "boolean", "If true, widget.data() will display as if it was a publish parameter.", null, { tags: ["Basic"] });
735
+
736
+ PropertyEditor.prototype.publish("sorting", "none", "set", "Specify the sorting type", ["none", "A-Z", "Z-A", "type"], { tags: ["Basic"], icons: ["fa-sort", "fa-sort-alpha-asc", "fa-sort-alpha-desc", "fa-sort-amount-asc"] });
737
+
738
+ PropertyEditor.prototype.publish("hideNonWidgets", false, "boolean", "Hides non-widget params (at this tier only)", null, { tags: ["Basic"] });
739
+
740
+ PropertyEditor.prototype.publish("label", "", "string", "Label to display in header of property editor table", null, { tags: ["Basic"] });
741
+ PropertyEditor.prototype.publish("filterTags", "", "set", "Only show Publish Params of this type", ["Basic", "Intermediate", "Advance", ""], {});
742
+ PropertyEditor.prototype.publish("excludeTags", ["Private"], "array", "Exclude this array of tags", null, {});
743
+ PropertyEditor.prototype.publish("excludeParams", [], "array", "Exclude this array of params (widget.param)", null, {});
744
+
745
+ PropertyEditor.prototype.publish("widget", null, "widget", "Widget", null, { tags: ["Basic"], render: false });
746
+
747
+ const _widgetOrig = PropertyEditor.prototype.widget;
748
+ (PropertyEditor.prototype as any).widget = function (_?: Widget): Widget | PropertyEditor {
749
+ if (arguments.length && _widgetOrig.call(this) === _) return this;
750
+ const retVal = _widgetOrig.apply(this, arguments);
751
+ if (arguments.length) {
752
+ this.watchWidget(_);
753
+ if (_ instanceof Grid) {
754
+ const context = this;
755
+ _.postSelectionChange = function () {
756
+ context._selectedItems = _._selectionBag.get().map(function (item) { return item.widget; });
757
+ context.lazyRender();
758
+ };
759
+ }
760
+ }
761
+ return retVal;
762
+ };