dazscript-framework 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/babel/babel.config.js +33 -0
- package/babel/trace-babel-plugin.js +243 -0
- package/babel/trace-log-babel-plugin.js +164 -0
- package/common/core.ts +2 -0
- package/common/log.ts +57 -0
- package/common/trace.ts +26 -0
- package/core/action-decorator.ts +16 -0
- package/dialog/basic-dialog.ts +32 -0
- package/dialog/builders/button-builder.ts +53 -0
- package/dialog/builders/checkbox-builder.ts +30 -0
- package/dialog/builders/combo-box-builder.ts +36 -0
- package/dialog/builders/combo-edit-builder.ts +36 -0
- package/dialog/builders/dialog-builder.ts +49 -0
- package/dialog/builders/groupbox-builder.ts +73 -0
- package/dialog/builders/label-builder.ts +18 -0
- package/dialog/builders/layout-builder.ts +46 -0
- package/dialog/builders/line-edit-builder.ts +118 -0
- package/dialog/builders/list-view-builder.ts +327 -0
- package/dialog/builders/node-selection-builder.ts +44 -0
- package/dialog/builders/popup-menu-builder.ts +47 -0
- package/dialog/builders/radio-builder.ts +43 -0
- package/dialog/builders/splitter-builder.ts +90 -0
- package/dialog/builders/tab-builder.ts +106 -0
- package/dialog/builders/widget-builder.ts +109 -0
- package/dialog/builders/widgets-builder.ts +119 -0
- package/dialog/input-dialog.ts +28 -0
- package/dialog/input-validator.ts +9 -0
- package/dialog/shared.ts +8 -0
- package/helpers/action-helper.ts +81 -0
- package/helpers/array-helper.ts +145 -0
- package/helpers/camera-helper.ts +14 -0
- package/helpers/custom-action-helper.ts +176 -0
- package/helpers/file-helper.ts +90 -0
- package/helpers/input-helper.ts +19 -0
- package/helpers/list-view-helper.ts +89 -0
- package/helpers/menu-helper.ts +29 -0
- package/helpers/message-box-helper.ts +16 -0
- package/helpers/node-helper.ts +216 -0
- package/helpers/number-helper.ts +11 -0
- package/helpers/numeric-property-helper.ts +92 -0
- package/helpers/object-helper.ts +3 -0
- package/helpers/pane-helper.ts +21 -0
- package/helpers/progress-helper.ts +34 -0
- package/helpers/property-helper.ts +53 -0
- package/helpers/record-helper.ts +16 -0
- package/helpers/scene-helper.ts +96 -0
- package/helpers/script-helper.ts +16 -0
- package/helpers/skeleton-helper.ts +27 -0
- package/helpers/splitter-helper.ts +9 -0
- package/helpers/string-helper.ts +28 -0
- package/helpers/surface-helper.ts +6 -0
- package/helpers/undo-helper.ts +7 -0
- package/helpers/viewport-helper.ts +9 -0
- package/lib/delayed.ts +39 -0
- package/lib/dz-dump.ts +121 -0
- package/lib/global.ts +5 -0
- package/lib/guid.ts +3 -0
- package/lib/observable.ts +94 -0
- package/lib/set.ts +25 -0
- package/lib/settings.ts +104 -0
- package/models/custom-action.ts +12 -0
- package/models/frame-keys.ts +68 -0
- package/package.json +48 -0
- package/shared/base-script.ts +30 -0
- package/shared/install-generator.js +185 -0
- package/shared/set-keyboard-shortcut.ts +103 -0
- package/webpack.config.js +48 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Observable } from '@dsf/lib/observable';
|
|
2
|
+
import { WidgetBuilderBase, createWidget } from './widget-builder';
|
|
3
|
+
import { WidgetBuilderContext } from './widgets-builder';
|
|
4
|
+
|
|
5
|
+
export class ComboEditBuilder extends WidgetBuilderBase<DzComboEdit> {
|
|
6
|
+
constructor(context: WidgetBuilderContext) {
|
|
7
|
+
super(createWidget(context).build(DzComboEdit));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
items(items: string[]): this {
|
|
11
|
+
this.widget.insertItems(0, items)
|
|
12
|
+
return this
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
changed(bind: Observable<string>): this {
|
|
16
|
+
this.widget.text = bind.value
|
|
17
|
+
this.widget.itemChanged.scriptConnect((text) => {
|
|
18
|
+
bind.value = text
|
|
19
|
+
})
|
|
20
|
+
bind.connect((text) => {
|
|
21
|
+
this.widget.text = text
|
|
22
|
+
})
|
|
23
|
+
return this
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
edited(bind: Observable<string>): this {
|
|
27
|
+
this.widget.text = bind.value
|
|
28
|
+
this.widget.textEdited.scriptConnect((text) => {
|
|
29
|
+
bind.value = text
|
|
30
|
+
})
|
|
31
|
+
bind.connect((text) => {
|
|
32
|
+
this.widget.text = text
|
|
33
|
+
})
|
|
34
|
+
return this
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { DialogProperties } from '../shared';
|
|
2
|
+
import { WidgetBuilderContext, WidgetsBuilder } from './widgets-builder';
|
|
3
|
+
|
|
4
|
+
export class DialogBuilder extends WidgetsBuilder {
|
|
5
|
+
public properties: DialogProperties
|
|
6
|
+
|
|
7
|
+
constructor(private title: string, private id?: string) {
|
|
8
|
+
super(new WidgetBuilderContext(new DzBasicDialog()))
|
|
9
|
+
this.properties = new DialogProperties()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
options(properties: DialogProperties) {
|
|
13
|
+
this.properties = properties
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
build(setup: () => void): DzBasicDialog {
|
|
17
|
+
this.init()
|
|
18
|
+
setup()
|
|
19
|
+
this.resize()
|
|
20
|
+
return this.context.dialog
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private init() {
|
|
24
|
+
this.context.dialog.caption = this.title
|
|
25
|
+
let dialogWidget = this.context.dialog.getWidget()
|
|
26
|
+
var key = (this.id ?? this.context.dialog.caption).replace(/ /g, "") + "Dlg"
|
|
27
|
+
dialogWidget.objectName = key
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private resize() {
|
|
31
|
+
let dialogWidget = this.context.dialog.getWidget();
|
|
32
|
+
let sizeHint = dialogWidget.minimumSizeHint;
|
|
33
|
+
let height = this.properties.height ?? sizeHint.height;
|
|
34
|
+
this.context.dialog.minHeight = sizeHint.height;
|
|
35
|
+
this.context.dialog.height = height;
|
|
36
|
+
|
|
37
|
+
if (this.properties.width) this.context.dialog.width = this.properties.width;
|
|
38
|
+
|
|
39
|
+
if (this.properties.resizable === true) {
|
|
40
|
+
this.context.dialog.sizeGripEnabled = true;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
if (this.properties.width)
|
|
44
|
+
this.context.dialog.setFixedWidth(this.properties.width);
|
|
45
|
+
this.context.dialog.setFixedHeight(height);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Observable } from '@dsf/lib/observable';
|
|
2
|
+
import { Direction } from '../shared';
|
|
3
|
+
import LayoutBuilder from './layout-builder';
|
|
4
|
+
import { IWidgetBuilder, createWidget } from './widget-builder';
|
|
5
|
+
import { WidgetBuilderContext } from './widgets-builder';
|
|
6
|
+
|
|
7
|
+
export default class GroupBoxBuilder implements IWidgetBuilder<DzGroupBox> {
|
|
8
|
+
private _title: Observable<string>
|
|
9
|
+
private _direction: Direction = 'vertical'
|
|
10
|
+
private _style: { flat?: boolean }
|
|
11
|
+
private _visible: Observable<boolean>
|
|
12
|
+
|
|
13
|
+
constructor(private context: WidgetBuilderContext) {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
title(text: string | Observable<string>): this {
|
|
17
|
+
this._title = typeof text === 'string'
|
|
18
|
+
? new Observable(text)
|
|
19
|
+
: text
|
|
20
|
+
return this
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
vertical(): this {
|
|
24
|
+
this._direction = 'vertical'
|
|
25
|
+
return this
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
horizontal(): this {
|
|
29
|
+
this._direction = 'horizontal'
|
|
30
|
+
return this
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
style(options: { flat?: boolean }): this {
|
|
34
|
+
this._style = options
|
|
35
|
+
return this
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
visible(value: boolean | Observable<boolean>): this {
|
|
39
|
+
this._visible = typeof value === 'boolean'
|
|
40
|
+
? new Observable(value)
|
|
41
|
+
: value
|
|
42
|
+
return this
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
build(then?: (layout: DzVBoxLayout | DzHBoxLayout) => void): DzGroupBox {
|
|
46
|
+
let groupBox = createWidget(this.context).build(DzGroupBox)
|
|
47
|
+
|
|
48
|
+
groupBox.title = this._title?.value
|
|
49
|
+
groupBox.flat = this._style?.flat
|
|
50
|
+
|
|
51
|
+
this._title?.connect((text) => {
|
|
52
|
+
groupBox.title = text
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
new LayoutBuilder(this.context)
|
|
56
|
+
.parent(groupBox)
|
|
57
|
+
.direction(this._direction)
|
|
58
|
+
.build(then)
|
|
59
|
+
|
|
60
|
+
if (this._visible) {
|
|
61
|
+
if (this._visible.value === false)
|
|
62
|
+
groupBox.hide()
|
|
63
|
+
this._visible.connect((visible) => {
|
|
64
|
+
if (visible)
|
|
65
|
+
groupBox.show()
|
|
66
|
+
else
|
|
67
|
+
groupBox.hide()
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return groupBox
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { WidgetBuilderBase, createWidget } from './widget-builder';
|
|
2
|
+
import { WidgetBuilderContext } from './widgets-builder';
|
|
3
|
+
|
|
4
|
+
export default class LabelBuilder extends WidgetBuilderBase<DzLabel> {
|
|
5
|
+
constructor(context: WidgetBuilderContext) {
|
|
6
|
+
super(createWidget(context).build(DzLabel));
|
|
7
|
+
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
text(text: string): this {
|
|
11
|
+
this.widget.text = text;
|
|
12
|
+
return this;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
build(): DzLabel {
|
|
16
|
+
return this.widget;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Direction } from '../shared';
|
|
2
|
+
import { WidgetBuilderContext } from './widgets-builder';
|
|
3
|
+
|
|
4
|
+
export default class LayoutBuilder {
|
|
5
|
+
private widgetParent: DzWidget | DzLayout | null;
|
|
6
|
+
private _direction: Direction = 'vertical';
|
|
7
|
+
|
|
8
|
+
constructor(protected context: WidgetBuilderContext) { }
|
|
9
|
+
|
|
10
|
+
static create(context: WidgetBuilderContext): LayoutBuilder {
|
|
11
|
+
return new LayoutBuilder(context)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
direction(direction: Direction): this {
|
|
15
|
+
this._direction = direction;
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
parent(parent: DzWidget | DzLayout): this {
|
|
20
|
+
this.widgetParent = parent;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
build(then?: (layout: DzVBoxLayout | DzHBoxLayout) => void): DzVBoxLayout | DzHBoxLayout {
|
|
25
|
+
let parent = this.widgetParent ?? this.context.parent;
|
|
26
|
+
let type = this.getLayoutFor(this._direction);
|
|
27
|
+
let layout = new type(parent);
|
|
28
|
+
|
|
29
|
+
let currentLayout = this.context.layout;
|
|
30
|
+
this.context.layout = layout;
|
|
31
|
+
|
|
32
|
+
if (parent.inherits("DzWidget") && (parent as any).addLayout)
|
|
33
|
+
(parent as DzBasicDialog).addLayout(layout);
|
|
34
|
+
|
|
35
|
+
then?.(layout);
|
|
36
|
+
|
|
37
|
+
this.context.layout = currentLayout;
|
|
38
|
+
return layout;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
private getLayoutFor(direction: string): new (arg: any) => DzVBoxLayout | DzHBoxLayout {
|
|
43
|
+
return direction === 'horizontal' ? DzHBoxLayout : DzVBoxLayout;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Observable } from '@dsf/lib/observable';
|
|
2
|
+
import { InputValidator } from '../input-validator';
|
|
3
|
+
import { WidgetBuilderBase, createWidget } from './widget-builder';
|
|
4
|
+
import { WidgetBuilderContext } from './widgets-builder';
|
|
5
|
+
|
|
6
|
+
export default class LineEditBuilder extends WidgetBuilderBase<DzLineEdit> {
|
|
7
|
+
private inputValidator: InputValidator
|
|
8
|
+
private minValue?: number
|
|
9
|
+
private maxValue?: number
|
|
10
|
+
|
|
11
|
+
constructor(context: WidgetBuilderContext) {
|
|
12
|
+
super(createWidget(context).build(DzLineEdit))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
placeholder(text: string): this {
|
|
16
|
+
this.widget.placeholderText = text
|
|
17
|
+
return this
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
focus(): this {
|
|
21
|
+
this.widget.getWidget().setFocus()
|
|
22
|
+
return this
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
validator(validator: InputValidator | null): this {
|
|
26
|
+
if (validator === null) return this
|
|
27
|
+
this.inputValidator = validator
|
|
28
|
+
let settings = new DzSettings()
|
|
29
|
+
settings.setStringValue("validator", InputValidator[validator]);
|
|
30
|
+
this.widget.setValidator(settings)
|
|
31
|
+
return this
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Sets the min value of the input when validator is set to in or float
|
|
36
|
+
* @param minValue
|
|
37
|
+
* @returns
|
|
38
|
+
*/
|
|
39
|
+
min(minValue: number): this {
|
|
40
|
+
this.minValue = minValue
|
|
41
|
+
return this
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Sets the max value of the input when validator is set to in or float
|
|
46
|
+
* @param maxValue
|
|
47
|
+
* @returns
|
|
48
|
+
*/
|
|
49
|
+
max(maxValue: number): this {
|
|
50
|
+
this.maxValue = maxValue
|
|
51
|
+
return this
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Clamps the input to min and max values. Only works when validator is set to int or float
|
|
56
|
+
* @param min
|
|
57
|
+
* @param max
|
|
58
|
+
* @returns
|
|
59
|
+
*/
|
|
60
|
+
clamp(min: number, max: number): this {
|
|
61
|
+
return this.min(min).max(max)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
readOnly(onOff: boolean | Observable<boolean>): this {
|
|
65
|
+
if (typeof onOff === 'boolean') {
|
|
66
|
+
this.widget.readOnly = onOff
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.widget.readOnly = onOff.value
|
|
70
|
+
onOff.connect((value) => {
|
|
71
|
+
this.widget.readOnly = value
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
return this
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
text(text_: string | Observable<string>): this {
|
|
78
|
+
if (typeof text_ === 'string') {
|
|
79
|
+
this.widget.text = text_
|
|
80
|
+
}
|
|
81
|
+
else if (text_ instanceof Observable) {
|
|
82
|
+
this.widget.text = text_.value;
|
|
83
|
+
this.widget.textChanged.scriptConnect((text: string) => {
|
|
84
|
+
text_.value = this.clampValue(text);
|
|
85
|
+
});
|
|
86
|
+
text_.connect((text: string) => {
|
|
87
|
+
this.widget.text = text;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private clampValue(text: string): string {
|
|
95
|
+
if (this.inputValidator === InputValidator.int || this.inputValidator === InputValidator.float) {
|
|
96
|
+
let value = Number(text);
|
|
97
|
+
if (!isNaN(value)) {
|
|
98
|
+
if (this.minValue !== undefined) {
|
|
99
|
+
value = Math.max(value, this.minValue);
|
|
100
|
+
}
|
|
101
|
+
if (this.maxValue !== undefined) {
|
|
102
|
+
value = Math.min(value, this.maxValue);
|
|
103
|
+
}
|
|
104
|
+
return String(value);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (this.maxValue && text.length > this.maxValue)
|
|
108
|
+
text = text[this.maxValue]
|
|
109
|
+
|
|
110
|
+
return text;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
build(then?: (lineEdit: DzLineEdit) => void): DzLineEdit {
|
|
115
|
+
then?.(this.widget);
|
|
116
|
+
return this.widget;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { warn } from '@dsf/common/log';
|
|
2
|
+
import { clearColumns, filter, getDataItem, setDataItem } from '@dsf/helpers/list-view-helper';
|
|
3
|
+
import { Delayed } from '@dsf/lib/delayed';
|
|
4
|
+
import { Observable } from '@dsf/lib/observable';
|
|
5
|
+
import { TreeNode } from 'shared/models/tree-node';
|
|
6
|
+
import { IWidgetBuilder, createWidget } from './widget-builder';
|
|
7
|
+
import { WidgetBuilderContext } from './widgets-builder';
|
|
8
|
+
|
|
9
|
+
type ListViewFilterOptions = {
|
|
10
|
+
keywords: Observable<string>,
|
|
11
|
+
field: (listItem: DzListViewItem) => string,
|
|
12
|
+
selectOnFilter?: boolean,
|
|
13
|
+
filters?: (viewItem: DzListViewItem) => boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export enum ListViewRefreshOptions {
|
|
17
|
+
All,
|
|
18
|
+
Filters
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class ListViewBuilder<TItem, TData> implements IWidgetBuilder<DzListView> {
|
|
22
|
+
|
|
23
|
+
private readonly context: ListViewBuilderContext<TItem, TData>
|
|
24
|
+
|
|
25
|
+
constructor(dialogContext: WidgetBuilderContext) {
|
|
26
|
+
this.context = new ListViewBuilderContext(dialogContext)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
visible(value: boolean | Observable<boolean>): this {
|
|
30
|
+
this.context.visible = typeof value === 'boolean'
|
|
31
|
+
? new Observable(value)
|
|
32
|
+
: value
|
|
33
|
+
return this
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Binds the list rows to a collection of items
|
|
38
|
+
* @param items
|
|
39
|
+
* @returns
|
|
40
|
+
*/
|
|
41
|
+
items(items: Observable<TreeNode<TItem>[]>): ListViewBindBuilder<TItem, TData> {
|
|
42
|
+
this.context.items = items
|
|
43
|
+
return new ListViewBindBuilder(this.context)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Build an item row with the provided function
|
|
48
|
+
* @param build the function to use for building a row
|
|
49
|
+
* @returns returns a ListViewItem or DzCheckListItem
|
|
50
|
+
*/
|
|
51
|
+
row(build: (item: TreeNode<TItem>, parent: DzListView | DzListViewItem, id?: number) => DzListViewItem): this {
|
|
52
|
+
this.context.rowBuilder = build
|
|
53
|
+
return this
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
sorting(sort: boolean | number): this {
|
|
57
|
+
if (typeof sort === 'boolean')
|
|
58
|
+
this.context.sorting = sort ? 0 : -1
|
|
59
|
+
else
|
|
60
|
+
this.context.sorting = sort
|
|
61
|
+
return this
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
expanded(onOff: boolean | Observable<boolean>): this {
|
|
65
|
+
if (typeof onOff === 'boolean')
|
|
66
|
+
this.context.expanded = new Observable(onOff)
|
|
67
|
+
else
|
|
68
|
+
this.context.expanded = onOff
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
flat(onOff: boolean | Observable<boolean>): this {
|
|
73
|
+
this.context.flat = typeof onOff === 'boolean'
|
|
74
|
+
? new Observable(onOff)
|
|
75
|
+
: onOff
|
|
76
|
+
return this
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
doubleClicked(bind: Observable<TData>): this {
|
|
80
|
+
this.context.doubleClicked = bind
|
|
81
|
+
return this
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
refresh(when: Observable<any>, what: ListViewRefreshOptions = ListViewRefreshOptions.All): this {
|
|
85
|
+
this.context.refreshWhen = when
|
|
86
|
+
this.context.refreshWhat = what
|
|
87
|
+
return this
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
contextMenu(fn: (listView: DzListView, item: DzListViewItem, pos: Point) => DzPopupMenu): this {
|
|
91
|
+
this.context.contextMenu = fn
|
|
92
|
+
return this
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
build(then?: (listView: DzListView) => void): DzListView {
|
|
96
|
+
let listView = build(this.context)
|
|
97
|
+
then?.(listView)
|
|
98
|
+
return listView
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
class ListViewBuilderContext<TItem, TData> {
|
|
103
|
+
columns: Observable<string[]> = new Observable([])
|
|
104
|
+
columnsWidth: (index: number, width: number) => number
|
|
105
|
+
items: Observable<TreeNode<TItem>[]> = new Observable([])
|
|
106
|
+
text: (item: TreeNode<TItem>) => string[]
|
|
107
|
+
data: (item: TreeNode<TItem>) => TData
|
|
108
|
+
sorting: number = 0
|
|
109
|
+
selected: Observable<TData>
|
|
110
|
+
filter: ListViewFilterOptions
|
|
111
|
+
doubleClicked: Observable<TData>
|
|
112
|
+
rowBuilder: (item: TreeNode<TItem>, parent: DzListView | DzListViewItem, id?: number) => DzListViewItem
|
|
113
|
+
contextMenu: (listView: DzListView, item: DzListViewItem, pos: Point) => DzPopupMenu
|
|
114
|
+
refreshWhen: Observable<void>
|
|
115
|
+
expanded: Observable<boolean>
|
|
116
|
+
visible: Observable<boolean> = new Observable(true)
|
|
117
|
+
decorated: boolean
|
|
118
|
+
flat: Observable<boolean>
|
|
119
|
+
refreshWhat: ListViewRefreshOptions = ListViewRefreshOptions.All
|
|
120
|
+
constructor(public readonly dialogContext: WidgetBuilderContext) { }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
class ListViewBindBuilder<TItem, TData> {
|
|
124
|
+
constructor(private context: ListViewBuilderContext<TItem, TData>) { }
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Sepecify the text to display on each column
|
|
128
|
+
* @param columns
|
|
129
|
+
* @returns
|
|
130
|
+
*/
|
|
131
|
+
columns(columns: string[] | Observable<string[]>, width?: (index: number, width: number) => number): this {
|
|
132
|
+
this.context.columns = columns instanceof Observable
|
|
133
|
+
? columns
|
|
134
|
+
: new Observable(columns)
|
|
135
|
+
this.context.columnsWidth = width
|
|
136
|
+
return this
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Callback for extracting the text to display on each column from every item in the list
|
|
141
|
+
* @param from the callback function to extract the text for each item
|
|
142
|
+
* @returns a string array for every column text to be display for a given item
|
|
143
|
+
*/
|
|
144
|
+
text(from: (item: TreeNode<TItem>) => string[]): this {
|
|
145
|
+
this.context.text = from
|
|
146
|
+
return this
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Callback to specify which part of the item will be stored in each row as data. It can be
|
|
151
|
+
* the whole item or a property of the item
|
|
152
|
+
* @param from the callback function to specify what to store as data
|
|
153
|
+
* @returns the data to be stored in each row for a given item
|
|
154
|
+
*/
|
|
155
|
+
data(from: (item: TreeNode<TItem>) => TData): this {
|
|
156
|
+
this.context.data = from
|
|
157
|
+
return this
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Bind the data of a selected row in the list to an object
|
|
162
|
+
* @param bind
|
|
163
|
+
* @returns
|
|
164
|
+
*/
|
|
165
|
+
selected(bind: Observable<TData>): this {
|
|
166
|
+
this.context.selected = bind
|
|
167
|
+
return this
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
filter(options: ListViewFilterOptions): this {
|
|
171
|
+
this.context.filter = options
|
|
172
|
+
return this
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
build(then?: (listView: DzListView) => void): DzListView {
|
|
176
|
+
let listView = build(this.context)
|
|
177
|
+
then?.(listView)
|
|
178
|
+
return listView
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const build = <TItem, TData>(context: ListViewBuilderContext<TItem, TData>): DzListView => {
|
|
183
|
+
const listView = createWidget(context.dialogContext).build(DzListView)
|
|
184
|
+
let rowId = -1
|
|
185
|
+
|
|
186
|
+
if (context.visible) {
|
|
187
|
+
if (context.visible.value === false)
|
|
188
|
+
listView.hide()
|
|
189
|
+
context.visible.connect((visible) => {
|
|
190
|
+
if (visible)
|
|
191
|
+
listView.show()
|
|
192
|
+
else
|
|
193
|
+
listView.hide()
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const buildColumns = (columns: string[]) => {
|
|
198
|
+
clearColumns(listView)
|
|
199
|
+
columns.forEach(column => {
|
|
200
|
+
listView.addColumn(column)
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const buildItem = (item: TreeNode<TItem>, parent: DzListView | DzListViewItem) => {
|
|
205
|
+
rowId++
|
|
206
|
+
parent = context.flat?.value === true ? listView : parent
|
|
207
|
+
|
|
208
|
+
let listItem = context.rowBuilder ? context.rowBuilder(item, parent, rowId) : new DzListViewItem(parent, rowId)
|
|
209
|
+
if (!listItem) return
|
|
210
|
+
|
|
211
|
+
listItem.open = context.expanded?.value === true
|
|
212
|
+
|
|
213
|
+
context.text(item).forEach((text, idx) => {
|
|
214
|
+
let data = context.data?.(item)
|
|
215
|
+
if (data) {
|
|
216
|
+
setDataItem(listItem, data)
|
|
217
|
+
}
|
|
218
|
+
if (!text || idx >= context.columns.value.length)
|
|
219
|
+
return;
|
|
220
|
+
listItem.setText(idx, text)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
item.children.forEach((child) => {
|
|
224
|
+
context.decorated = true
|
|
225
|
+
buildItem(child, listItem)
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const filterList = (keywords?: string) => {
|
|
230
|
+
filter(listView, context.filter.field, keywords ?? context.filter?.keywords?.value, { selectOnFilter: context.filter.selectOnFilter ?? true, filters: context.filter.filters })
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const buildList = (items: TreeNode<TItem>[], selectedId?: number) => {
|
|
234
|
+
rowId = -1
|
|
235
|
+
if (!context.text)
|
|
236
|
+
return warn('No text function provided for list builder')
|
|
237
|
+
|
|
238
|
+
listView.clear()
|
|
239
|
+
context.columns?.value.forEach((_, index) => {
|
|
240
|
+
listView.setColumnWidth(index, 0)
|
|
241
|
+
})
|
|
242
|
+
listView.allColumnsShowFocus = true
|
|
243
|
+
|
|
244
|
+
if (context.visible.value === false)
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
if (!items) return
|
|
248
|
+
|
|
249
|
+
items.forEach((item) => {
|
|
250
|
+
buildItem(item, listView)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
if (context.columnsWidth) {
|
|
254
|
+
context.columns?.value.forEach((_, index) => {
|
|
255
|
+
listView.setColumnWidth(index, context.columnsWidth(index, listView.columnWidth(index)))
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
listView.rootIsDecorated = context.decorated
|
|
260
|
+
listView.setSorting(context.sorting)
|
|
261
|
+
if (context.sorting >= 0) listView.sort()
|
|
262
|
+
|
|
263
|
+
if (context.filter?.keywords.value || context.filter?.filters)
|
|
264
|
+
filterList()
|
|
265
|
+
|
|
266
|
+
if (selectedId) {
|
|
267
|
+
listView.getItems(DzListView.All).forEach(item => {
|
|
268
|
+
if (item.id === selectedId) {
|
|
269
|
+
listView.setSelected(item, true)
|
|
270
|
+
listView.setCurrentItem(item)
|
|
271
|
+
listView.ensureItemVisible(item)
|
|
272
|
+
}
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const onSelectionChanged = (callback: (item: DzListViewItem, data: TData) => void) => {
|
|
278
|
+
(listView as any)["selectionChanged()"].scriptConnect(() => {
|
|
279
|
+
callback(listView.selectedItem(), getDataItem(listView.selectedItem()))
|
|
280
|
+
})
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
buildColumns(context.columns.value)
|
|
284
|
+
context.columns.connect((columns) => {
|
|
285
|
+
buildColumns(columns)
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
buildList(context.items?.value)
|
|
289
|
+
context.items.connect((items) => {
|
|
290
|
+
buildList(items, listView.selectedItem()?.id)
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
context.refreshWhen?.connect(() => {
|
|
294
|
+
if (context.refreshWhat === ListViewRefreshOptions.All)
|
|
295
|
+
buildList(context.items?.value, listView.selectedItem()?.id)
|
|
296
|
+
else {
|
|
297
|
+
filterList()
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
if (context.selected) {
|
|
303
|
+
onSelectionChanged((_, data) => {
|
|
304
|
+
context.selected.value = data
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (context.filter) {
|
|
309
|
+
context.filter.keywords.connect((keywords) => {
|
|
310
|
+
new Delayed(() => {
|
|
311
|
+
filterList(keywords)
|
|
312
|
+
}, 100, 400).trigger()
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (context.doubleClicked) {
|
|
317
|
+
listView.doubleClicked.scriptConnect((listItem) => {
|
|
318
|
+
context.doubleClicked.value = listItem?.getDataItem('data')
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
listView.contextMenuRequested.scriptConnect((item: DzListViewItem, pos: Point) => {
|
|
323
|
+
context.contextMenu?.(listView, item, pos).exec(pos.cursorPos(), 0)
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
return listView
|
|
327
|
+
}
|