uiconfig-blueprint 0.1.0-dev.6 → 0.1.0-dev.7
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/dist/{allPaths-CxAGUqQA.js → allPaths-BOo4cECg.js} +5 -5
- package/dist/{allPaths-CxAGUqQA.js.map → allPaths-BOo4cECg.js.map} +1 -1
- package/dist/{allPathsLoader-D9qLQ797.js → allPathsLoader-B4vYL2gG.js} +4 -4
- package/dist/{allPathsLoader-D9qLQ797.js.map → allPathsLoader-B4vYL2gG.js.map} +1 -1
- package/dist/bpComponents/BPButtonComponent.d.ts +1 -0
- package/dist/bpComponents/BPColorInputComponent.d.ts +1 -0
- package/dist/bpComponents/BPComponent.d.ts +1 -0
- package/dist/bpComponents/BPFileComponent.d.ts +1 -0
- package/dist/bpComponents/BPFolderComponent.d.ts +5 -0
- package/dist/bpComponents/BPTreeComponent.d.ts +9 -3
- package/dist/components/InspectorStackComponent.d.ts +2 -1
- package/dist/components/Tree2.d.ts +52 -0
- package/dist/components/TreeNode2.d.ts +43 -0
- package/dist/components/treeTypes.d.ts +18 -0
- package/dist/{index-CKe0TkQl.js → index-13b-jSvJ.js} +5259 -4992
- package/dist/index-13b-jSvJ.js.map +1 -0
- package/dist/{index-CWeYtJnn.js → index-B0IO--x7.js} +2 -2
- package/dist/{index-CWeYtJnn.js.map → index-B0IO--x7.js.map} +1 -1
- package/dist/{index-Dv22bSeb.js → index-BttMe1vP.js} +2 -2
- package/dist/{index-Dv22bSeb.js.map → index-BttMe1vP.js.map} +1 -1
- package/dist/index.js +43 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{splitPathsBySizeLoader-DjI12iKG.js → splitPathsBySizeLoader-nUv2DYaP.js} +5 -5
- package/dist/{splitPathsBySizeLoader-DjI12iKG.js.map → splitPathsBySizeLoader-nUv2DYaP.js.map} +1 -1
- package/docs/classes/UI.html +3 -3
- package/docs/classes/UiConfigRendererBlueprint.html +3 -3
- package/lib/css/renderer.css +77 -10
- package/lib/css/renderer.css.map +1 -1
- package/lib/esm/ConfigObject.js +8 -4
- package/lib/esm/ConfigObject.js.map +1 -1
- package/lib/esm/UiConfigRendererBlueprint.js +22 -11
- package/lib/esm/UiConfigRendererBlueprint.js.map +1 -1
- package/lib/esm/bpComponents/BPButtonComponent.d.ts +1 -0
- package/lib/esm/bpComponents/BPButtonComponent.js +4 -2
- package/lib/esm/bpComponents/BPButtonComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPColorInputComponent.d.ts +1 -0
- package/lib/esm/bpComponents/BPColorInputComponent.js +3 -1
- package/lib/esm/bpComponents/BPColorInputComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPComponent.d.ts +1 -0
- package/lib/esm/bpComponents/BPComponent.js +4 -2
- package/lib/esm/bpComponents/BPComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPDropdownInputComponent.js +2 -2
- package/lib/esm/bpComponents/BPDropdownInputComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPFileComponent.d.ts +1 -0
- package/lib/esm/bpComponents/BPFileComponent.js +7 -1
- package/lib/esm/bpComponents/BPFileComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPFolderComponent.d.ts +5 -0
- package/lib/esm/bpComponents/BPFolderComponent.js +46 -6
- package/lib/esm/bpComponents/BPFolderComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPInputComponent.js +2 -1
- package/lib/esm/bpComponents/BPInputComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPNumberInputComponent.js +1 -1
- package/lib/esm/bpComponents/BPNumberInputComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPTextInputComponent.js +1 -1
- package/lib/esm/bpComponents/BPTextInputComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPToggleInputComponent.js +3 -2
- package/lib/esm/bpComponents/BPToggleInputComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPTreeComponent.d.ts +9 -3
- package/lib/esm/bpComponents/BPTreeComponent.js +14 -3
- package/lib/esm/bpComponents/BPTreeComponent.js.map +1 -1
- package/lib/esm/components/ExtendedNumericInput.js +1 -1
- package/lib/esm/components/ExtendedNumericInput.js.map +1 -1
- package/lib/esm/components/InspectorStackComponent.d.ts +2 -1
- package/lib/esm/components/InspectorStackComponent.js +2 -2
- package/lib/esm/components/InspectorStackComponent.js.map +1 -1
- package/lib/esm/components/Tree2.d.ts +52 -0
- package/lib/esm/components/Tree2.js +177 -0
- package/lib/esm/components/Tree2.js.map +1 -0
- package/lib/esm/components/TreeNode2.d.ts +43 -0
- package/lib/esm/components/TreeNode2.js +111 -0
- package/lib/esm/components/TreeNode2.js.map +1 -0
- package/lib/esm/components/treeTypes.d.ts +18 -0
- package/lib/esm/components/treeTypes.js +2 -0
- package/lib/esm/components/treeTypes.js.map +1 -0
- package/package.json +2 -2
- package/src/ConfigObject.tsx +11 -4
- package/src/UiConfigRendererBlueprint.tsx +22 -11
- package/src/bpComponents/BPButtonComponent.scss +3 -1
- package/src/bpComponents/BPButtonComponent.tsx +8 -2
- package/src/bpComponents/BPColorInputComponent.tsx +7 -1
- package/src/bpComponents/BPComponent.tsx +3 -0
- package/src/bpComponents/BPDropdownInputComponent.tsx +2 -1
- package/src/bpComponents/BPFileComponent.tsx +13 -2
- package/src/bpComponents/BPFolderComponent.scss +8 -3
- package/src/bpComponents/BPFolderComponent.tsx +85 -13
- package/src/bpComponents/BPInputComponent.tsx +4 -1
- package/src/bpComponents/BPNumberInputComponent.tsx +2 -1
- package/src/bpComponents/BPTextInputComponent.tsx +1 -0
- package/src/bpComponents/BPToggleInputComponent.tsx +6 -3
- package/src/bpComponents/BPTreeComponent.tsx +21 -5
- package/src/components/ExtendedNumericInput.tsx +1 -1
- package/src/components/InspectorStackComponent.tsx +2 -2
- package/src/components/Tree2.tsx +344 -0
- package/src/components/TreeNode2.tsx +192 -0
- package/src/components/common.scss +3 -0
- package/src/components/tree2.scss +42 -0
- package/src/components/treeTypes.ts +97 -0
- package/src/renderer.scss +39 -6
- package/dist/index-CKe0TkQl.js.map +0 -1
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
background-color: $light-gray4 !important;
|
|
30
30
|
}
|
|
31
31
|
.bp5-dark .folder-card {
|
|
32
|
-
background-color: $dark-
|
|
32
|
+
background-color: $dark-gray1 !important;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
|
|
@@ -68,11 +68,15 @@
|
|
|
68
68
|
background-color: $dark-gray1 !important;
|
|
69
69
|
|
|
70
70
|
&:hover {
|
|
71
|
-
background-color: $dark-
|
|
71
|
+
background-color: $dark-gray2 !important;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
&:active {
|
|
75
|
-
background-color: $dark-
|
|
75
|
+
background-color: $dark-gray2 !important;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
&.folder-trigger-button-expanded{
|
|
79
|
+
background-color: $dark-gray2 !important;
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
|
|
@@ -93,6 +97,7 @@
|
|
|
93
97
|
//margin-left: $pt-grid-size;
|
|
94
98
|
padding-top: 6px;
|
|
95
99
|
padding-bottom: 6px;
|
|
100
|
+
min-height: 36px;
|
|
96
101
|
}
|
|
97
102
|
}
|
|
98
103
|
|
|
@@ -5,6 +5,9 @@ import {ConfigObject} from "../ConfigObject";
|
|
|
5
5
|
import {PanelActions} from "@blueprintjs/core/lib/esm/components/panel-stack2/panelTypes";
|
|
6
6
|
import {safeSetProperty} from 'ts-browser-helpers'
|
|
7
7
|
import {BPContainerComponent, BPContainerComponentState} from './BPContainerComponent'
|
|
8
|
+
import classNames from "classnames";
|
|
9
|
+
import {Classes} from "@blueprintjs/core/src/common";
|
|
10
|
+
import {AnimationStates} from "@blueprintjs/core/lib/esm/components/collapse/collapse";
|
|
8
11
|
|
|
9
12
|
export type BPFolderComponentState = BPContainerComponentState & {
|
|
10
13
|
}
|
|
@@ -39,18 +42,21 @@ export class BPFolderComponent extends BPContainerComponent<BPFolderComponentSta
|
|
|
39
42
|
enabled={enabledToggle ? this.context.methods.getRawValue(enabledToggle) : undefined}
|
|
40
43
|
onEnabledChange={(e) => enabledToggle && this.context.methods.setValue(enabledToggle, e.target.checked, {}).then(() => this.setState(this.state))}
|
|
41
44
|
>
|
|
42
|
-
<
|
|
45
|
+
<Collapse2 isOpen={this.state.expanded} keepChildrenMounted={true} transitionDuration={300}>
|
|
43
46
|
<div className="folder-children" style={{listStyleType: "none", paddingLeft: this.props.level??0 > 2 ? "6px" : 0}}> {/*todo use parameter instead of const 6*/}
|
|
44
|
-
{this.state.children.map((c, i) =>
|
|
45
|
-
|
|
47
|
+
{this.state.children.map((c, i) =>
|
|
48
|
+
<ConfigObject key={'c' + i} {...this.props} config={c}
|
|
49
|
+
level={(this.props.level ?? 0) + 1}/>
|
|
50
|
+
)}
|
|
46
51
|
</div>
|
|
47
|
-
</
|
|
52
|
+
</Collapse2>
|
|
48
53
|
</FolderHeadCard>
|
|
49
54
|
) : null
|
|
50
55
|
}
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
export const FolderHeadCard: React.FC<React.PropsWithChildren<{ open: boolean, label: string, minimal: boolean, level: number, disabled?: boolean, enabled?: boolean, onEnabledChange?: ChangeEventHandler<HTMLInputElement>, onClick: DOMAttributes<HTMLElement>['onClick'] }>> = (props) => {
|
|
59
|
+
const hasEnabled = (props.enabled !== undefined)
|
|
54
60
|
return (
|
|
55
61
|
<div
|
|
56
62
|
// interactive={!props.open}
|
|
@@ -76,7 +82,7 @@ export const FolderHeadCard: React.FC<React.PropsWithChildren<{ open: boolean, l
|
|
|
76
82
|
{/*</Button>*/}
|
|
77
83
|
|
|
78
84
|
<Button
|
|
79
|
-
className="folder-trigger-button"
|
|
85
|
+
className={"folder-trigger-button " + (props.open ? "folder-trigger-button-expanded" : "")}
|
|
80
86
|
// fill={!props.minimal}
|
|
81
87
|
fill={true}
|
|
82
88
|
onClick={props.onClick}
|
|
@@ -85,16 +91,18 @@ export const FolderHeadCard: React.FC<React.PropsWithChildren<{ open: boolean, l
|
|
|
85
91
|
small={props.minimal}
|
|
86
92
|
style={props.level ? {marginLeft: "6px"} : {fontSize: "0.95rem", paddingTop: "8px", paddingBottom: "8px"}}
|
|
87
93
|
intent={props.open ? Intent.PRIMARY : Intent.NONE}
|
|
88
|
-
icon={props.enabled !== undefined && <span style={{minWidth: '20px'}}></span>} // adding a span here will center the text in the button
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
// icon={props.enabled !== undefined && <span style={{minWidth: '20px'}}></span>} // adding a span here will center the text in the button
|
|
95
|
+
icon={(
|
|
96
|
+
<>
|
|
97
|
+
<Icon icon="caret-right" style={{
|
|
98
|
+
rotate: props.open ? "90deg" : "0deg",
|
|
99
|
+
transition: "rotate 0.25s ease-in-out"
|
|
100
|
+
}}/>
|
|
101
|
+
</>
|
|
94
102
|
)}>{props.label}
|
|
95
103
|
</Button>
|
|
96
|
-
{
|
|
97
|
-
style={{margin: 0, position: 'absolute', left: '
|
|
104
|
+
{hasEnabled && <Checkbox
|
|
105
|
+
style={{margin: 0, position: 'absolute', left: '10px'}}
|
|
98
106
|
large inline
|
|
99
107
|
defaultChecked={props.enabled}
|
|
100
108
|
onChange={props.onEnabledChange}
|
|
@@ -106,3 +114,67 @@ export const FolderHeadCard: React.FC<React.PropsWithChildren<{ open: boolean, l
|
|
|
106
114
|
</div>
|
|
107
115
|
)
|
|
108
116
|
}
|
|
117
|
+
|
|
118
|
+
export class Collapse2 extends Collapse{
|
|
119
|
+
private contentsRefHandler2 = (el: HTMLElement | null) => {
|
|
120
|
+
// @ts-ignore
|
|
121
|
+
this.contents = el;
|
|
122
|
+
// @ts-ignore
|
|
123
|
+
if (this.contents != null) {
|
|
124
|
+
// @ts-ignore
|
|
125
|
+
const height = this.contents.clientHeight;
|
|
126
|
+
this.setState({
|
|
127
|
+
animationState: this.props.isOpen ? AnimationStates.OPEN : AnimationStates.CLOSED,
|
|
128
|
+
height: height === 0 ? undefined : `${height}px`,
|
|
129
|
+
heightWhenOpen: height === 0 ? undefined : height,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
public render() {
|
|
135
|
+
const isContentVisible = this.state.animationState !== AnimationStates.CLOSED;
|
|
136
|
+
const shouldRenderChildren = isContentVisible || this.props.keepChildrenMounted;
|
|
137
|
+
// const displayWithTransform = isContentVisible && this.state.animationState !== AnimationStates.CLOSING;
|
|
138
|
+
const isAutoHeight = this.state.height === "auto";
|
|
139
|
+
|
|
140
|
+
const containerStyle = {
|
|
141
|
+
height: isContentVisible ? this.state.height : undefined,
|
|
142
|
+
overflowY: isAutoHeight ? "visible" : undefined,
|
|
143
|
+
// transitions don't work with height: auto
|
|
144
|
+
transition: isAutoHeight ? "none" : undefined,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const contentsStyle = {
|
|
148
|
+
// only use heightWhenOpen while closing
|
|
149
|
+
// transform: displayWithTransform ? "translateY(0)" : `translateY(-${this.state.heightWhenOpen}px)`,
|
|
150
|
+
// transitions don't work with height: auto
|
|
151
|
+
// transition: isAutoHeight ? "none" : undefined,
|
|
152
|
+
display: "block", // for opacity animation
|
|
153
|
+
visibility: isContentVisible ? "visible" : "hidden",
|
|
154
|
+
opacity: this.state.animationState === AnimationStates.CLOSING || this.state.animationState === AnimationStates.CLOSED ? 0.1 : 1,
|
|
155
|
+
} as React.CSSProperties;
|
|
156
|
+
|
|
157
|
+
// if(isContentVisible && !isAutoHeight && this.state.heightWhenOpen && this.state.height){
|
|
158
|
+
// const h = parseInt(this.state.height)
|
|
159
|
+
// contentsStyle.opacity = h / this.state.heightWhenOpen;
|
|
160
|
+
// console.log(contentsStyle.opacity)
|
|
161
|
+
// }
|
|
162
|
+
|
|
163
|
+
return React.createElement(
|
|
164
|
+
this.props.component!,
|
|
165
|
+
{
|
|
166
|
+
className: classNames(Classes.COLLAPSE, this.props.className),
|
|
167
|
+
style: containerStyle,
|
|
168
|
+
},
|
|
169
|
+
<div
|
|
170
|
+
className={Classes.COLLAPSE_BODY}
|
|
171
|
+
ref={this.contentsRefHandler2}
|
|
172
|
+
style={contentsStyle}
|
|
173
|
+
aria-hidden={!isContentVisible}
|
|
174
|
+
>
|
|
175
|
+
{shouldRenderChildren ? this.props.children : null}
|
|
176
|
+
</div>,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
}
|
|
@@ -26,7 +26,10 @@ export abstract class BPInputComponent<
|
|
|
26
26
|
|
|
27
27
|
render() {
|
|
28
28
|
return !this.state.hidden ? (
|
|
29
|
-
<FormGroupComponent disabled={this.state.disabled}
|
|
29
|
+
<FormGroupComponent disabled={this.state.disabled}
|
|
30
|
+
label={this.state.label}
|
|
31
|
+
flexBasis={this.state.baseWidth ?? this.flexBasis}
|
|
32
|
+
>
|
|
30
33
|
{this.renderInput()}
|
|
31
34
|
</FormGroupComponent>
|
|
32
35
|
) : null
|
|
@@ -27,9 +27,10 @@ export class BPNumberInputComponent extends BPInputComponent<number> {
|
|
|
27
27
|
disabled={this.state.disabled}
|
|
28
28
|
readOnly={this.state.readOnly}
|
|
29
29
|
defaultValue={this.state.value}
|
|
30
|
+
fill={true}
|
|
30
31
|
ref={this._inputs[0]}
|
|
31
32
|
key={this.props.config.uuid}
|
|
32
|
-
onChange2={this._onChange}
|
|
33
|
+
onChange2={this._onChange}/>
|
|
33
34
|
)
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -16,14 +16,17 @@ export class BPToggleInputComponent extends BPInputComponent<boolean> {
|
|
|
16
16
|
return (
|
|
17
17
|
<Switch alignIndicator="right" defaultChecked={this.state.value}
|
|
18
18
|
disabled={this.state.disabled} readOnly={this.state.readOnly}
|
|
19
|
-
label={this.state.label} onChange={this._onChange}
|
|
20
|
-
|
|
19
|
+
labelElement={(<span className={"bp5-label"} title={this.state.label}>{this.state.label}</span>)} onChange={this._onChange}
|
|
20
|
+
inline={false} large={false}
|
|
21
|
+
className="switch"/>
|
|
21
22
|
)
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
render() {
|
|
25
26
|
return !this.state.hidden ? (
|
|
26
|
-
<div className="xPaddedContent folderContent"
|
|
27
|
+
<div className="xPaddedContent folderContent"
|
|
28
|
+
style={{flexBasis: this.state.baseWidth ?? this.flexBasis}}
|
|
29
|
+
>
|
|
27
30
|
{this.renderInput()}
|
|
28
31
|
</div>
|
|
29
32
|
) : null
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import {Tree2} from "../components/Tree2";
|
|
1
2
|
import {BPComponent, BPComponentProps, BPComponentState, UiConfigRendererContextType} from "./BPComponent";
|
|
2
|
-
import {
|
|
3
|
+
import {TreeNodeInfo} from "@blueprintjs/core";
|
|
4
|
+
import React from "react";
|
|
3
5
|
|
|
4
6
|
export type BPTreeComponentState<T = {}> = BPComponentState & {
|
|
5
7
|
nodes: TreeNodeInfo<T>[]
|
|
@@ -8,8 +10,8 @@ export type BPTreeComponentState<T = {}> = BPComponentState & {
|
|
|
8
10
|
// https://github.com/palantir/blueprint/blob/develop/packages/docs-app/src/examples/core-examples/treeExample.tsx
|
|
9
11
|
type NodePath = (string|number)[];
|
|
10
12
|
|
|
11
|
-
export abstract class BPTreeComponent<T = {}, TConfigVal extends any /*|PrimitiveVal|void*/ = void> extends BPComponent<TConfigVal, BPTreeComponentState<T
|
|
12
|
-
constructor(props: BPComponentProps<TConfigVal
|
|
13
|
+
export abstract class BPTreeComponent<T = {}, TConfigVal extends any /*|PrimitiveVal|void*/ = void> extends BPComponent<TConfigVal, BPTreeComponentState<T>, BPComponentProps<TConfigVal> & {className: string}> {
|
|
14
|
+
constructor(props: BPComponentProps<TConfigVal> & {className: string}, context: UiConfigRendererContextType) {
|
|
13
15
|
super(props, context, {nodes: []});
|
|
14
16
|
}
|
|
15
17
|
|
|
@@ -189,8 +191,15 @@ export abstract class BPTreeComponent<T = {}, TConfigVal extends any /*|Primitiv
|
|
|
189
191
|
e.stopPropagation()
|
|
190
192
|
}
|
|
191
193
|
|
|
194
|
+
protected _canDropNode(_sourceNode: TreeNodeInfo<T>, _sourcePath: NodePath, _targetNode: TreeNodeInfo<T>, _targetPath: NodePath, _index?: number) {
|
|
195
|
+
return true
|
|
196
|
+
}
|
|
197
|
+
protected _onDropNode(_sourceNode: TreeNodeInfo<T>, _sourcePath: NodePath, _targetNode: TreeNodeInfo<T>, _targetPath: NodePath, _e?: React.DragEvent, _index?: number) {
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
192
201
|
render() {
|
|
193
|
-
const TreeT =
|
|
202
|
+
const TreeT = Tree2.ofType<T>()
|
|
194
203
|
return !this.state.hidden ? (
|
|
195
204
|
<div
|
|
196
205
|
style={{width: "100%", height: "100%"}}
|
|
@@ -199,7 +208,14 @@ export abstract class BPTreeComponent<T = {}, TConfigVal extends any /*|Primitiv
|
|
|
199
208
|
>
|
|
200
209
|
<TreeT
|
|
201
210
|
contents={this.state.nodes}
|
|
202
|
-
className="folderContent"
|
|
211
|
+
className={"folderContent " + (this.props.className||'')}
|
|
212
|
+
canDropNode={(node, path, targetNode, targetPath, index) => {
|
|
213
|
+
return this._canDropNode(node, path, targetNode, targetPath, index)
|
|
214
|
+
}}
|
|
215
|
+
onNodeDrop={(node, path, targetNode, targetPath, e, index) => {
|
|
216
|
+
console.log(node, path, targetNode, targetPath, e);
|
|
217
|
+
this._onDropNode(node, path, targetNode, targetPath, e, index)
|
|
218
|
+
}}
|
|
203
219
|
onNodeExpand={(node, _path, _e) => {
|
|
204
220
|
this._onNodeExpandCollapse(node.id)
|
|
205
221
|
}}
|
|
@@ -49,7 +49,7 @@ export class ExtendedNumericInput extends React.PureComponent<HTMLInputProps & N
|
|
|
49
49
|
|
|
50
50
|
public render() {
|
|
51
51
|
const {value} = this.state;
|
|
52
|
-
const props2: HTMLInputProps & NumericInputProps & { onChange2?: (v: number, last?: boolean) => void } = {...this.props}
|
|
52
|
+
const props2: HTMLInputProps & NumericInputProps & { onChange2?: (v: number, last?: boolean) => void } = {...this.props, leftIcon: undefined}
|
|
53
53
|
if (props2.onChange2) delete props2.onChange2
|
|
54
54
|
|
|
55
55
|
return (
|
|
@@ -6,7 +6,7 @@ import {UiConfigRendererContext} from '../bpComponents/BPComponent'
|
|
|
6
6
|
|
|
7
7
|
// import {ViewerAppContext} from "./ViewerAppComponent";
|
|
8
8
|
|
|
9
|
-
export function InspectorStackComponent({config}: { config: UiObjectConfig<any, 'panel'
|
|
9
|
+
export function InspectorStackComponent({config, className}: { config: UiObjectConfig<any, 'panel'>, className?: string }) {
|
|
10
10
|
const renderer = React.useContext(UiConfigRendererContext)
|
|
11
11
|
const stackItem = React.useCallback(() => {
|
|
12
12
|
return {
|
|
@@ -30,7 +30,7 @@ export function InspectorStackComponent({config}: { config: UiObjectConfig<any,
|
|
|
30
30
|
}, [setCurrentPanelStack, stackItem])
|
|
31
31
|
|
|
32
32
|
return (
|
|
33
|
-
<Card className="bpInspectorCard" style={{borderRadius: 0}}>
|
|
33
|
+
<Card className={"bpInspectorCard " + className||''} style={{borderRadius: 0}}>
|
|
34
34
|
<PanelStack2 className="inspectorPanelStack"
|
|
35
35
|
showPanelHeader={true}
|
|
36
36
|
renderActivePanelOnly={true}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2015 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
* Changes:
|
|
17
|
+
* - Added support for drag and drop
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
import classNames from "classnames";
|
|
21
|
+
import * as React from "react";
|
|
22
|
+
|
|
23
|
+
import type {TreeEventHandler} from "@blueprintjs/core";
|
|
24
|
+
import {Classes, DISPLAYNAME_PREFIX, type Props} from "@blueprintjs/core";
|
|
25
|
+
import {TreeNode2} from "./TreeNode2";
|
|
26
|
+
import {TREE_NODE} from "@blueprintjs/core/lib/esnext/common/classes";
|
|
27
|
+
import { TreeNodeInfo } from "./treeTypes";
|
|
28
|
+
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
30
|
+
export interface TreeProps<T = {}> extends Props {
|
|
31
|
+
/**
|
|
32
|
+
* Whether to use a compact appearance which reduces the visual padding around node content.
|
|
33
|
+
*/
|
|
34
|
+
compact?: boolean;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The data specifying the contents and appearance of the tree.
|
|
38
|
+
*/
|
|
39
|
+
contents: ReadonlyArray<TreeNodeInfo<T>>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Invoked when a node is clicked anywhere other than the caret for expanding/collapsing the node.
|
|
43
|
+
*/
|
|
44
|
+
onNodeClick?: TreeEventHandler<T>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Invoked when caret of an expanded node is clicked.
|
|
48
|
+
*/
|
|
49
|
+
onNodeCollapse?: TreeEventHandler<T>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Invoked when a node is right-clicked or the context menu button is pressed on a focused node.
|
|
53
|
+
*/
|
|
54
|
+
onNodeContextMenu?: TreeEventHandler<T>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Invoked when a node is double-clicked. Be careful when using this in combination with
|
|
58
|
+
* an `onNodeClick` (single-click) handler, as the way this behaves can vary between browsers.
|
|
59
|
+
* See http://stackoverflow.com/q/5497073/3124288
|
|
60
|
+
*/
|
|
61
|
+
onNodeDoubleClick?: TreeEventHandler<T>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Invoked when the caret of a collapsed node is clicked.
|
|
65
|
+
*/
|
|
66
|
+
onNodeExpand?: TreeEventHandler<T>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Invoked when the mouse is moved over a node.
|
|
70
|
+
*/
|
|
71
|
+
onNodeMouseEnter?: TreeEventHandler<T>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Invoked when the mouse is moved out of a node.
|
|
75
|
+
*/
|
|
76
|
+
onNodeMouseLeave?: TreeEventHandler<T>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Invoked when a node is dropped onto another node.
|
|
80
|
+
*/
|
|
81
|
+
onNodeDrop?: (source: TreeNodeInfo<T>, sourcePath: number[], target: TreeNodeInfo<T>, targetPath: number[], event: React.DragEvent<HTMLElement>, index?: number) => void;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Invoked when a node drag starts.
|
|
85
|
+
*/
|
|
86
|
+
onNodeDragStart?: (node: TreeNodeInfo<T>, path: number[], event: React.DragEvent<HTMLElement>) => void;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Invoked when a node is dragged over another node.
|
|
90
|
+
*/
|
|
91
|
+
onNodeDragOver?: (node: TreeNodeInfo<T>, path: number[], event: React.DragEvent<HTMLElement>) => void;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Invoked when a node drag ends.
|
|
95
|
+
*/
|
|
96
|
+
onNodeDragEnd?: (node: TreeNodeInfo<T>, path: number[], event: React.DragEvent<HTMLElement>) => void;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Invoked when a dragged node enters another node.
|
|
100
|
+
*/
|
|
101
|
+
onNodeDragEnter?: (node: TreeNodeInfo<T>, path: number[], event: React.DragEvent<HTMLElement>) => void;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Invoked when a dragged node leaves another node.
|
|
105
|
+
*/
|
|
106
|
+
onNodeDragLeave?: (node: TreeNodeInfo<T>, path: number[], event: React.DragEvent<HTMLElement>) => void;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* A function that determines whether a node can be dropped onto another node.
|
|
110
|
+
* @param source
|
|
111
|
+
* @param target
|
|
112
|
+
*/
|
|
113
|
+
canDropNode?: (source: TreeNodeInfo<T>, sourcePath: number[], target: TreeNodeInfo<T>, targetPath: number[], index?: number) => boolean;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Tree component.
|
|
118
|
+
*
|
|
119
|
+
* @see https://blueprintjs.com/docs/#core/components/tree
|
|
120
|
+
*/
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
122
|
+
export class Tree2<T = {}> extends React.Component<TreeProps<T>> {
|
|
123
|
+
public static displayName = `${DISPLAYNAME_PREFIX}.Tree`;
|
|
124
|
+
|
|
125
|
+
public static ofType<U>() {
|
|
126
|
+
return Tree2 as new (props: TreeProps<U>) => Tree2<U>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public static nodeFromPath<U>(
|
|
130
|
+
path: readonly number[],
|
|
131
|
+
treeNodes?: ReadonlyArray<TreeNodeInfo<U>>,
|
|
132
|
+
): TreeNodeInfo<U> {
|
|
133
|
+
if (path.length === 1) {
|
|
134
|
+
return treeNodes![path[0]];
|
|
135
|
+
} else {
|
|
136
|
+
return Tree2.nodeFromPath(path.slice(1), treeNodes![path[0]].childNodes);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private nodeRefs: { [nodeId: string]: HTMLElement } = {};
|
|
141
|
+
private dragSource: { node: TreeNodeInfo<T>; path: number[] } | null = null;
|
|
142
|
+
private draggingNode?: { node: TreeNodeInfo<T>; path: number[] };
|
|
143
|
+
private dragOverNode?: { node: TreeNodeInfo<T>; path: number[]; currentTarget?: HTMLElement };
|
|
144
|
+
private rootRef: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>();
|
|
145
|
+
|
|
146
|
+
public render() {
|
|
147
|
+
return (
|
|
148
|
+
<div
|
|
149
|
+
className={classNames(Classes.TREE, this.props.className, {
|
|
150
|
+
[Classes.COMPACT]: this.props.compact,
|
|
151
|
+
})}
|
|
152
|
+
ref={this.rootRef}
|
|
153
|
+
>
|
|
154
|
+
{this.renderNodes(this.props.contents, [], Classes.TREE_ROOT)}
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Returns the underlying HTML element of the `Tree` node with an id of `nodeId`.
|
|
161
|
+
* This element does not contain the children of the node, only its label and controls.
|
|
162
|
+
* If the node is not currently mounted, `undefined` is returned.
|
|
163
|
+
*/
|
|
164
|
+
public getNodeContentElement(nodeId: string | number): HTMLElement | undefined {
|
|
165
|
+
return this.nodeRefs[nodeId];
|
|
166
|
+
}
|
|
167
|
+
private renderNodeSpacer = (node: TreeNodeInfo<T>, path: number[], i: number)=> {
|
|
168
|
+
return <li className={Classes.TREE + "-drop-spacer"}
|
|
169
|
+
key={"drop-" + node.id + "-" + i}
|
|
170
|
+
>
|
|
171
|
+
<div
|
|
172
|
+
onDragOver={(e) => this.handleNodeDragOver(node, path, e, i)}
|
|
173
|
+
onDrop={(e) => this.handleNodeDrop(node, path, e, i)}
|
|
174
|
+
onDragEnter={(e) => this.handleNodeDragEnter(node, path, e, i)}
|
|
175
|
+
onDragLeave={(e) => this.handleNodeDragLeave(node, path, e, i)}
|
|
176
|
+
></div>
|
|
177
|
+
</li>
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private renderNodes(
|
|
181
|
+
treeNodes: ReadonlyArray<TreeNodeInfo<T>> | undefined,
|
|
182
|
+
currentPath?: number[],
|
|
183
|
+
className?: string,
|
|
184
|
+
parentNode?: TreeNodeInfo<T>,
|
|
185
|
+
) {
|
|
186
|
+
if (treeNodes == null) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const nodeItems = treeNodes.map((node, i) => {
|
|
191
|
+
const elementPath = currentPath!.concat(i);
|
|
192
|
+
const draggable = !!node.draggable;
|
|
193
|
+
const droppable = !!node.droppable;
|
|
194
|
+
const parentDroppable = !!(parentNode && parentNode.droppable);
|
|
195
|
+
return (<>
|
|
196
|
+
{parentDroppable && i === 0 && this.renderNodeSpacer(parentNode, elementPath, i)}
|
|
197
|
+
<TreeNode2<T>
|
|
198
|
+
{...node}
|
|
199
|
+
key={node.id}
|
|
200
|
+
contentRef={this.handleContentRef}
|
|
201
|
+
depth={elementPath.length - 1}
|
|
202
|
+
onClick={this.handleNodeClick}
|
|
203
|
+
onContextMenu={this.handleNodeContextMenu}
|
|
204
|
+
onCollapse={this.handleNodeCollapse}
|
|
205
|
+
onDoubleClick={this.handleNodeDoubleClick}
|
|
206
|
+
onExpand={this.handleNodeExpand}
|
|
207
|
+
onMouseEnter={this.handleNodeMouseEnter}
|
|
208
|
+
onMouseLeave={this.handleNodeMouseLeave}
|
|
209
|
+
path={elementPath}
|
|
210
|
+
draggable={draggable}
|
|
211
|
+
onDragStart={draggable ? this.handleNodeDragStart : undefined}
|
|
212
|
+
onDragEnd={draggable ? this.handleNodeDragEnd : undefined}
|
|
213
|
+
onDragOver={droppable ? this.handleNodeDragOver : undefined}
|
|
214
|
+
onDrop={droppable ? this.handleNodeDrop : undefined}
|
|
215
|
+
onDragEnter={droppable ? this.handleNodeDragEnter : undefined}
|
|
216
|
+
onDragLeave={droppable ? this.handleNodeDragLeave : undefined}
|
|
217
|
+
>
|
|
218
|
+
{this.renderNodes(node.childNodes, elementPath, undefined, node)}
|
|
219
|
+
</TreeNode2>
|
|
220
|
+
{parentDroppable && i >= 0 && this.renderNodeSpacer(parentNode, elementPath, i + 1)}
|
|
221
|
+
</>)
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return <ul className={classNames(Classes.TREE_NODE_LIST, className)}>{nodeItems}</ul>;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private handleContentRef = (node: TreeNodeInfo<T>, element: HTMLElement | null) => {
|
|
228
|
+
if (element != null) {
|
|
229
|
+
this.nodeRefs[node.id] = element;
|
|
230
|
+
} else {
|
|
231
|
+
// don't want our object to get bloated with old keys
|
|
232
|
+
delete this.nodeRefs[node.id];
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
private handleNodeCollapse = (node: TreeNodeInfo<T>, path: number[], e: React.MouseEvent<HTMLElement>) => {
|
|
237
|
+
this.props.onNodeCollapse?.(node, path, e);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
private handleNodeClick = (node: TreeNodeInfo<T>, path: number[], e: React.MouseEvent<HTMLElement>) => {
|
|
241
|
+
this.props.onNodeClick?.(node, path, e);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
private handleNodeContextMenu = (node: TreeNodeInfo<T>, path: number[], e: React.MouseEvent<HTMLElement>) => {
|
|
245
|
+
this.props.onNodeContextMenu?.(node, path, e);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
private handleNodeDoubleClick = (node: TreeNodeInfo<T>, path: number[], e: React.MouseEvent<HTMLElement>) => {
|
|
249
|
+
this.props.onNodeDoubleClick?.(node, path, e);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
private handleNodeExpand = (node: TreeNodeInfo<T>, path: number[], e: React.MouseEvent<HTMLElement>) => {
|
|
253
|
+
this.props.onNodeExpand?.(node, path, e);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
private handleNodeMouseEnter = (node: TreeNodeInfo<T>, path: number[], e: React.MouseEvent<HTMLElement>) => {
|
|
257
|
+
this.props.onNodeMouseEnter?.(node, path, e);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
private handleNodeMouseLeave = (node: TreeNodeInfo<T>, path: number[], e: React.MouseEvent<HTMLElement>) => {
|
|
261
|
+
this.props.onNodeMouseLeave?.(node, path, e);
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Drag and drop handlers
|
|
265
|
+
private handleNodeDragStart = (node: TreeNodeInfo<T>, path: number[], e: React.DragEvent<HTMLElement>) => {
|
|
266
|
+
this.dragSource = { node, path };
|
|
267
|
+
this.draggingNode = { node, path };
|
|
268
|
+
this.rootRef?.current?.classList.add(Classes.TREE + "-dragging-node");
|
|
269
|
+
this.addNodeClass(node, TREE_NODE + "-dragging");
|
|
270
|
+
e.dataTransfer.effectAllowed = "move";
|
|
271
|
+
try {
|
|
272
|
+
e.dataTransfer.setData("application/json", JSON.stringify({ nodeId: node.id, path }));
|
|
273
|
+
} catch {
|
|
274
|
+
// ignore
|
|
275
|
+
}
|
|
276
|
+
this.props.onNodeDragStart?.(node, path, e);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
private handleNodeDragOver = (node: TreeNodeInfo<T>, path: number[], e: React.DragEvent<HTMLElement>, index?: number) => {
|
|
280
|
+
if (this.dragSource && this.props.canDropNode) {
|
|
281
|
+
const canDrop = this.props.canDropNode(this.dragSource.node, this.dragSource.path, node, path, index);
|
|
282
|
+
if (!canDrop) {
|
|
283
|
+
e.dataTransfer.dropEffect = "none";
|
|
284
|
+
e.preventDefault();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const currentTarget = e.currentTarget as HTMLElement;
|
|
289
|
+
if ((this.dragOverNode?.node.id !== node.id || this.dragOverNode?.currentTarget !== currentTarget) && this.draggingNode?.node.id !== node.id) {
|
|
290
|
+
if (this.dragOverNode) this.removeNodeClass(this.dragOverNode.node, TREE_NODE + "-dragover", this.dragOverNode.currentTarget);
|
|
291
|
+
this.dragOverNode = { node, path, currentTarget };
|
|
292
|
+
this.addNodeClass(this.dragOverNode.node, TREE_NODE + "-dragover", this.dragOverNode.currentTarget);
|
|
293
|
+
}
|
|
294
|
+
e.preventDefault();
|
|
295
|
+
e.dataTransfer.dropEffect = "move";
|
|
296
|
+
this.props.onNodeDragOver?.(node, path, e);
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
private handleNodeDrop = (targetNode: TreeNodeInfo<T>, targetPath: number[], e: React.DragEvent<HTMLElement>, index?: number) => {
|
|
300
|
+
e.preventDefault();
|
|
301
|
+
const source = this.dragSource
|
|
302
|
+
const canDrop = !source ? false : (this.props.canDropNode ? this.props.canDropNode(source.node, source.path, targetNode, targetPath, index) : true);
|
|
303
|
+
if (this.dragOverNode) this.removeNodeClass(this.dragOverNode.node, TREE_NODE + "-dragover", this.dragOverNode.currentTarget);
|
|
304
|
+
if (this.draggingNode) this.removeNodeClass(this.draggingNode.node, TREE_NODE + "-dragging");
|
|
305
|
+
this.dragSource = null;
|
|
306
|
+
this.draggingNode = undefined;
|
|
307
|
+
this.rootRef?.current?.classList.remove(Classes.TREE + "-dragging-node");
|
|
308
|
+
this.dragOverNode = undefined;
|
|
309
|
+
if (canDrop && source && this.props.onNodeDrop) {
|
|
310
|
+
this.props.onNodeDrop(source.node, source.path, targetNode, targetPath, e, index);
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
private handleNodeDragEnd = (node: TreeNodeInfo<T>, path: number[], e: React.DragEvent<HTMLElement>, _index?: number) => {
|
|
315
|
+
if (this.draggingNode) this.removeNodeClass(this.draggingNode.node, TREE_NODE + "-dragging");
|
|
316
|
+
if (this.dragOverNode) this.removeNodeClass(this.dragOverNode.node, TREE_NODE + "-dragover", this.dragOverNode.currentTarget);
|
|
317
|
+
this.dragSource = null;
|
|
318
|
+
this.draggingNode = undefined;
|
|
319
|
+
this.rootRef?.current?.classList.remove(Classes.TREE + "-dragging-node");
|
|
320
|
+
this.dragOverNode = undefined;
|
|
321
|
+
this.props.onNodeDragEnd?.(node, path, e);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
private handleNodeDragEnter = (node: TreeNodeInfo<T>, path: number[], e: React.DragEvent<HTMLElement>, _index?: number) => {
|
|
325
|
+
this.props.onNodeDragEnter?.(node, path, e);
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
private handleNodeDragLeave = (node: TreeNodeInfo<T>, path: number[], e: React.DragEvent<HTMLElement>, _index?: number) => {
|
|
329
|
+
if (this.dragOverNode) this.removeNodeClass(this.dragOverNode.node, TREE_NODE + "-dragover", this.dragOverNode.currentTarget);
|
|
330
|
+
this.dragOverNode = undefined;
|
|
331
|
+
this.props.onNodeDragLeave?.(node, path, e);
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
private addNodeClass = (node: TreeNodeInfo<T>|number[], className: string, currentTarget?: HTMLElement) => {
|
|
335
|
+
const el = this.nodeRefs[!Array.isArray(node) ? node.id : Tree2.nodeFromPath(node, this.props.contents).id];
|
|
336
|
+
if (el) el.classList.add(className);
|
|
337
|
+
if(currentTarget) currentTarget.classList.add(className);
|
|
338
|
+
}
|
|
339
|
+
private removeNodeClass = (node: TreeNodeInfo<T>|number[], className: string, currentTarget?: HTMLElement) => {
|
|
340
|
+
const el = this.nodeRefs[!Array.isArray(node) ? node.id : Tree2.nodeFromPath(node, this.props.contents).id];
|
|
341
|
+
if (el) el.classList.remove(className);
|
|
342
|
+
if(currentTarget) currentTarget.classList.remove(className);
|
|
343
|
+
}
|
|
344
|
+
}
|