uiconfig-blueprint 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +129 -0
  3. package/dist/ConfigObject.d.ts +16 -0
  4. package/dist/UiConfigRendererBlueprint.d.ts +13 -0
  5. package/dist/allPaths-PklnL3F6.js +23 -0
  6. package/dist/allPaths-PklnL3F6.js.map +1 -0
  7. package/dist/allPathsLoader-2dOu0jQ3.js +27 -0
  8. package/dist/allPathsLoader-2dOu0jQ3.js.map +1 -0
  9. package/dist/bpComponents/BPButtonComponent.d.ts +8 -0
  10. package/dist/bpComponents/BPColorInputComponent.d.ts +25 -0
  11. package/dist/bpComponents/BPComponent.d.ts +25 -0
  12. package/dist/bpComponents/BPContainerComponent.d.ts +21 -0
  13. package/dist/bpComponents/BPDropdownInputComponent.d.ts +16 -0
  14. package/dist/bpComponents/BPFileComponent.d.ts +26 -0
  15. package/dist/bpComponents/BPFolderComponent.d.ts +16 -0
  16. package/dist/bpComponents/BPInputComponent.d.ts +11 -0
  17. package/dist/bpComponents/BPLabelledComponent.d.ts +8 -0
  18. package/dist/bpComponents/BPNumberInputComponent.d.ts +11 -0
  19. package/dist/bpComponents/BPPanelComponent.d.ts +17 -0
  20. package/dist/bpComponents/BPSliderInputComponent.d.ts +15 -0
  21. package/dist/bpComponents/BPTextInputComponent.d.ts +8 -0
  22. package/dist/bpComponents/BPToggleInputComponent.d.ts +9 -0
  23. package/dist/bpComponents/BPTreeComponent.d.ts +32 -0
  24. package/dist/bpComponents/BPTreeFolderComponent.d.ts +10 -0
  25. package/dist/bpComponents/BPValueComponent.d.ts +16 -0
  26. package/dist/bpComponents/BPVectorInputComponent.d.ts +20 -0
  27. package/dist/components/DraggableIcon.d.ts +17 -0
  28. package/dist/components/ExtendedNumericInput.d.ts +23 -0
  29. package/dist/components/FileComponent.d.ts +7 -0
  30. package/dist/components/FormGroupComponent.d.ts +6 -0
  31. package/dist/components/InspectorStackComponent.d.ts +5 -0
  32. package/dist/components/VisualStyleContext.d.ts +110 -0
  33. package/dist/components/bpIconWrapper.d.ts +2 -0
  34. package/dist/components/variableIcons.d.ts +41 -0
  35. package/dist/contexts.d.ts +1 -0
  36. package/dist/index-PTojlie-.js +29271 -0
  37. package/dist/index-PTojlie-.js.map +1 -0
  38. package/dist/index-iM9IChdK.js +604 -0
  39. package/dist/index-iM9IChdK.js.map +1 -0
  40. package/dist/index-tqa0R-I0.js +604 -0
  41. package/dist/index-tqa0R-I0.js.map +1 -0
  42. package/dist/index.d.ts +4 -0
  43. package/dist/index.js +292 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/index.mjs +12 -0
  46. package/dist/index.mjs.map +1 -0
  47. package/dist/splitPathsBySizeLoader-QNLjzk6A.js +36 -0
  48. package/dist/splitPathsBySizeLoader-QNLjzk6A.js.map +1 -0
  49. package/dist/threejs.d.ts +2 -0
  50. package/docs/.nojekyll +1 -0
  51. package/docs/assets/highlight.css +120 -0
  52. package/docs/assets/main.js +58 -0
  53. package/docs/assets/search.js +1 -0
  54. package/docs/assets/style.css +1279 -0
  55. package/docs/classes/UI.html +403 -0
  56. package/docs/classes/UiConfigRendererBlueprint.html +404 -0
  57. package/docs/index.html +134 -0
  58. package/docs/modules.html +50 -0
  59. package/package.json +127 -0
  60. package/src/ConfigObject.tsx +137 -0
  61. package/src/UiConfigRendererBlueprint.tsx +85 -0
  62. package/src/bpComponents/BPButtonComponent.scss +8 -0
  63. package/src/bpComponents/BPButtonComponent.tsx +27 -0
  64. package/src/bpComponents/BPColorInputComponent.tsx +138 -0
  65. package/src/bpComponents/BPComponent.tsx +93 -0
  66. package/src/bpComponents/BPContainerComponent.tsx +57 -0
  67. package/src/bpComponents/BPDropdownInputComponent.tsx +80 -0
  68. package/src/bpComponents/BPFileComponent.tsx +73 -0
  69. package/src/bpComponents/BPFolderComponent.scss +107 -0
  70. package/src/bpComponents/BPFolderComponent.tsx +91 -0
  71. package/src/bpComponents/BPInputComponent.tsx +33 -0
  72. package/src/bpComponents/BPLabelledComponent.tsx +19 -0
  73. package/src/bpComponents/BPNumberInputComponent.tsx +35 -0
  74. package/src/bpComponents/BPPanelComponent.tsx +50 -0
  75. package/src/bpComponents/BPSliderInputComponent.tsx +71 -0
  76. package/src/bpComponents/BPTextInputComponent.tsx +26 -0
  77. package/src/bpComponents/BPToggleInputComponent.tsx +31 -0
  78. package/src/bpComponents/BPTreeComponent.scss +3 -0
  79. package/src/bpComponents/BPTreeComponent.tsx +127 -0
  80. package/src/bpComponents/BPTreeFolderComponent.tsx +31 -0
  81. package/src/bpComponents/BPValueComponent.tsx +46 -0
  82. package/src/bpComponents/BPVectorInputComponent.tsx +163 -0
  83. package/src/bpComponents/index.scss +3 -0
  84. package/src/components/DraggableIcon.tsx +75 -0
  85. package/src/components/ExtendedNumericInput.tsx +219 -0
  86. package/src/components/FileComponent.tsx +79 -0
  87. package/src/components/FormGroupComponent.tsx +22 -0
  88. package/src/components/InspectorStackComponent.tsx +72 -0
  89. package/src/components/VisualStyleContext.tsx +69 -0
  90. package/src/components/bpIconWrapper.tsx +20 -0
  91. package/src/components/common.scss +21 -0
  92. package/src/components/variableIcons.tsx +177 -0
  93. package/src/contexts.ts +1 -0
  94. package/src/index.ts +8 -0
  95. package/src/renderer.scss +193 -0
  96. package/src/threejs.ts +3 -0
  97. package/tsconfig.json +40 -0
@@ -0,0 +1,75 @@
1
+ import * as React from "react";
2
+ import {Icon, IconName, MaybeElement} from "@blueprintjs/core";
3
+
4
+ export class DraggableIcon extends React.Component<{ icon: IconName | MaybeElement, value: number, stepSize?: number, onChange: (v: number, last?: boolean) => void }> {
5
+ private cursor = 'auto'
6
+ private dragStartOffset = -Infinity
7
+ private dragStartValue = -Infinity
8
+ private lastValue = -Infinity
9
+ private removeEvents = () => {
10
+ if (isFinite(this.lastValue)) this.props.onChange(this.lastValue)
11
+ this.lastValue = -Infinity
12
+ this.dragStartOffset = -Infinity
13
+ this.dragStartValue = -Infinity
14
+ document.removeEventListener('mousemove', this.mouseMoveEvent)
15
+ document.removeEventListener('mouseup', this.mouseUpEvent)
16
+ document.documentElement.style.cursor = this.cursor
17
+ // setDragging(false)
18
+ };
19
+ private mouseUpEvent = (ev: MouseEvent) => {
20
+ ev.stopPropagation()
21
+ ev.preventDefault()
22
+ this.removeEvents()
23
+ };
24
+ private mouseMoveEvent = (ev: MouseEvent) => {
25
+ ev.stopPropagation()
26
+ ev.preventDefault()
27
+ if (ev.buttons > 0) {
28
+ if (isFinite(this.dragStartValue) && isFinite(this.dragStartOffset)) {
29
+ let stepSize = (this.props.stepSize ?? 1) / 10
30
+ let curr = ev.clientX;
31
+ let mm = 100;
32
+ let off = this.dragStartOffset - curr;
33
+ // off = Math.min(mm, Math.max(-mm, off));
34
+ off = off / mm; //-1, 1
35
+ // off = (off + 1)/2;
36
+ // off *= (props.max - props.min) / 2
37
+ off *= ((stepSize) * 100) / 2
38
+ if (ev.shiftKey) off *= 10
39
+ if (ev.altKey) off *= 0.1
40
+
41
+ let val = this.dragStartValue - off;
42
+ // val = Math.min(props.max, Math.max(props.min, val)); // todo
43
+ val = Number(val.toFixed(Math.max(1, -Math.log10(stepSize / 10)))); // clip decimal places depending on step size
44
+ this.props.onChange(val, false)
45
+ this.lastValue = val;
46
+ }
47
+ } else {
48
+ this.removeEvents()
49
+ // setDragging(false)
50
+ }
51
+ };
52
+
53
+ render() {
54
+ // const [dragging, setDragging] = React.useState(false)
55
+
56
+ return (
57
+ <div style={{cursor: 'ew-resize', margin: "6px"}}
58
+ onMouseDown={(ev) => {
59
+ // if(dragging) return
60
+ this.dragStartOffset = ev.clientX
61
+ this.dragStartValue = this.props.value
62
+ ev.stopPropagation()
63
+ ev.preventDefault()
64
+ this.cursor = document.documentElement.style.cursor
65
+ document.documentElement.style.cursor = "ew-resize"
66
+ document.addEventListener('mousemove', this.mouseMoveEvent)
67
+ document.addEventListener('mouseup', this.mouseUpEvent)
68
+ // setDragging(true)
69
+ }}
70
+ >
71
+ <Icon icon={this.props.icon}/>
72
+ </div>
73
+ )
74
+ }
75
+ }
@@ -0,0 +1,219 @@
1
+ /*
2
+ * Copyright 2017 Palantir Technologies, Inc. All rights reserved.
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+
16
+ //https://github.com/palantir/blueprint/blob/develop/packages/docs-app/src/examples/core-examples/numericInputExtendedExample.tsx
17
+
18
+ import React from "react";
19
+
20
+ import type {HTMLInputProps, NumericInputProps} from "@blueprintjs/core";
21
+ import {NumericInput} from "@blueprintjs/core";
22
+ import {DraggableIcon} from "./DraggableIcon";
23
+
24
+ export interface IExtendedNumericInputState {
25
+ value?: string;
26
+ }
27
+
28
+ const NumberAbbreviation = {
29
+ BILLION: "b",
30
+ MILLION: "m",
31
+ THOUSAND: "k",
32
+ };
33
+
34
+ const NUMBER_ABBREVIATION_REGEX = /((\.\d+)|(\d+(\.\d+)?))(k|m|b)\b/gi;
35
+ const SCIENTIFIC_NOTATION_REGEX = /((\.\d+)|(\d+(\.\d+)?))(e\d+)\b/gi;
36
+
37
+ export class ExtendedNumericInput extends React.PureComponent<HTMLInputProps & NumericInputProps & { onChange2: (v: number, last?: boolean) => void }, IExtendedNumericInputState> {
38
+ public state: IExtendedNumericInputState = {
39
+ value: (this.props.value ?? this.props.defaultValue ?? '').toString(),
40
+ };
41
+
42
+ async setValue(val?: number) {
43
+ return new Promise<void>((resolve) => {
44
+ this.setState({value: (val ?? this.props.value ?? this.props.defaultValue ?? '').toString()}, resolve)
45
+ })
46
+ }
47
+
48
+ public render() {
49
+ const {value} = this.state;
50
+ const props2: HTMLInputProps & NumericInputProps & { onChange2?: (v: number, last?: boolean) => void } = {...this.props}
51
+ if (props2.onChange2) delete props2.onChange2
52
+
53
+ return (
54
+ <NumericInput
55
+ {...props2}
56
+ leftElement={(
57
+ <DraggableIcon icon={this.props.leftIcon ?? "variable"}
58
+ value={parseFloat(value ?? '0')}
59
+ stepSize={this.props.stepSize}
60
+ onChange={(v, last) => {
61
+ this.handleValueChange(v, v.toString(), null, last, true)
62
+ }}/>
63
+ )}
64
+ // leftIcon={"variable"}
65
+ allowNumericCharactersOnly={false}
66
+ onBlur={this.handleBlur}
67
+ onKeyDown={this.handleKeyDown}
68
+ onValueChange={this.handleValueChange}
69
+ placeholder="Enter a number or expression..."
70
+ value={value}
71
+ />
72
+ );
73
+ }
74
+
75
+ private handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
76
+ this.handleConfirm((e.target as HTMLInputElement).value);
77
+ };
78
+
79
+ private handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
80
+ if (e.code === 'Enter') {
81
+ this.handleConfirm((e.target as HTMLInputElement).value);
82
+ }
83
+ };
84
+
85
+ private handleValueChange = (_valueAsNumber: number, valueAsString: string, inputElement: HTMLInputElement | null, last?: boolean, dispatchOnChange2 = false) => {
86
+ this.setState({value: valueAsString}, () => {
87
+ this.props.onValueChange?.(_valueAsNumber, valueAsString, inputElement)
88
+ if(dispatchOnChange2)
89
+ this.props.onChange2?.(_valueAsNumber, last)
90
+ });
91
+ };
92
+
93
+ private handleConfirm = (value: string) => {
94
+ let result = value;
95
+ result = this.expandScientificNotationTerms(result);
96
+ result = this.expandNumberAbbreviationTerms(result);
97
+ result = this.evaluateSimpleMathExpression(result);
98
+ result = this.nanStringToEmptyString(result);
99
+ this.setState({value: result}, () => {
100
+ this.props.onValueChange?.(parseFloat(result) || 0, result, null)
101
+ this.props.onChange2?.(parseFloat(result) || 0)
102
+ });
103
+
104
+ // the user could have typed a different expression that evaluates to
105
+ // the same value. force the update to ensure a render triggers even if
106
+ // this is the case.
107
+ this.forceUpdate();
108
+ };
109
+
110
+ private expandScientificNotationTerms = (value: string) => {
111
+ // leave empty strings empty
112
+ if (!value) {
113
+ return value;
114
+ }
115
+ return value.replace(SCIENTIFIC_NOTATION_REGEX, this.expandScientificNotationNumber);
116
+ };
117
+
118
+ private expandNumberAbbreviationTerms = (value: string) => {
119
+ // leave empty strings empty
120
+ if (!value) {
121
+ return value;
122
+ }
123
+ return value.replace(NUMBER_ABBREVIATION_REGEX, this.expandAbbreviatedNumber);
124
+ };
125
+
126
+ // Adapted from http://stackoverflow.com/questions/2276021/evaluating-a-string-as-a-mathematical-expression-in-javascript
127
+ // maybe try a better solution from above, right now it only works left to right
128
+ private evaluateSimpleMathExpression = (value: string) => {
129
+ // leave empty strings empty
130
+ if (!value) {
131
+ return value;
132
+ }
133
+
134
+ // parse all terms from the expression. split on the +, -, *, /, ^(pow) characters and then
135
+ // validate that each term is a number.
136
+ const terms = value.split(/[+\-*\/^]/);
137
+
138
+ // ex. "1 + 2 - 3 # 4" will parse on the + and - signs into
139
+ // ["1 ", " 2 ", " 3 # 4"]. after trimming whitespace from each term
140
+ // and coercing them to numbers, the third term will become NaN,
141
+ // indicating that there was some illegal character present in it.
142
+ const trimmedTerms = terms.map((term: string) => term.trim());
143
+ const numericTerms = trimmedTerms.map((term: string) => +term);
144
+ const illegalTerms = numericTerms.filter(isNaN);
145
+
146
+ if (illegalTerms.length > 0) {
147
+ return "";
148
+ }
149
+
150
+ // evaluate the expression now that we know it's valid
151
+ let total = 0;
152
+
153
+ // the regex below will match decimal numbers--optionally preceded by
154
+ // +-*/^ followed by any number of spaces—-including each of the
155
+ // following:
156
+ // ".1"
157
+ // " 1"
158
+ // "1.1"
159
+ // "+ 1"
160
+ // "- 1.1"
161
+ const matches = value.match(/[+\-*\/^]*\s*(\.\d+|\d+(\.\d+)?)/gi) || [];
162
+ for (const match of matches) {
163
+ const compactedMatch = match.replace(/\s/g, "");
164
+ const compactedMatchNum = compactedMatch.replace(/[*\/^]/g, "");
165
+ const num = parseFloat(compactedMatchNum);
166
+ if(compactedMatch.startsWith("*")) total *= num;
167
+ else if(compactedMatch.startsWith("/")) total /= num;
168
+ else if(compactedMatch.startsWith("^")) total = Math.pow(total, num);
169
+ else total += num;
170
+ }
171
+ const roundedTotal = this.roundValue(total);
172
+ return roundedTotal.toString();
173
+ };
174
+
175
+ private nanStringToEmptyString = (value: string) => {
176
+ // our evaluation logic isn't perfect, so use this as a final
177
+ // sanitization step if the result was not a number.
178
+ return value === "NaN" ? "" : value;
179
+ };
180
+
181
+ private expandAbbreviatedNumber = (value: string) => {
182
+ if (!value) {
183
+ return value;
184
+ }
185
+
186
+ const num = +value.substring(0, value.length - 1);
187
+ const lastChar = value.charAt(value.length - 1).toLowerCase();
188
+
189
+ let result: number | null = null;
190
+
191
+ if (lastChar === NumberAbbreviation.THOUSAND) {
192
+ result = num * 1e3;
193
+ } else if (lastChar === NumberAbbreviation.MILLION) {
194
+ result = num * 1e6;
195
+ } else if (lastChar === NumberAbbreviation.BILLION) {
196
+ result = num * 1e9;
197
+ }
198
+
199
+ const isValid = result != null && !isNaN(result);
200
+
201
+ if (isValid) {
202
+ result = this.roundValue(result!);
203
+ }
204
+
205
+ return isValid ? result!.toString() : "";
206
+ };
207
+
208
+ private expandScientificNotationNumber = (value: string) => {
209
+ if (!value) {
210
+ return value;
211
+ }
212
+ return (+value).toString();
213
+ };
214
+
215
+ private roundValue = (value: number, precision: number = 1) => {
216
+ // round to at most two decimal places
217
+ return Math.round(value * 10 ** precision) / 10 ** precision;
218
+ };
219
+ }
@@ -0,0 +1,79 @@
1
+ import React, {useEffect, useRef} from "react";
2
+ import {Button, ControlGroup, FileInput, HTMLSelect, InputGroup} from "@blueprintjs/core";
3
+ import {BPFileComponentState} from "../bpComponents/BPFileComponent";
4
+
5
+ export interface FileComponentProps {
6
+ state: BPFileComponentState
7
+ onChange: (state: Partial<BPFileComponentState>)=>void
8
+ }
9
+ export const FileComponent: React.FC<FileComponentProps> = ({state, onChange})=>{
10
+ const fileInputRef = useRef<HTMLInputElement>()
11
+ const textInputRef = useRef<HTMLInputElement>()
12
+ // console.log('render file component', state)
13
+ useEffect(() => {
14
+ const fileElement = fileInputRef.current;
15
+ const textElement = textInputRef.current;
16
+ const val = state.value
17
+ if(!val) {
18
+ fileElement && (fileElement.value = '')
19
+ textElement && (textElement.value = '')
20
+ return
21
+ }
22
+ if(typeof val === 'string') {
23
+ textElement && (textElement.value = val)
24
+ return
25
+ }
26
+ if(!fileElement) return;
27
+ // console.log(val)
28
+ const dataTransfer = new DataTransfer();
29
+ dataTransfer.items.add(val instanceof File ? val : new File([val], (val as any).name||('file'+((val as any).ext?'.'+(val as any).ext:'')))); // it can be Blob also because of GLTF loaded...
30
+ fileElement!.files = dataTransfer.files
31
+ }, [state.value]);
32
+ // useEffect(() => {
33
+ // fileInputRef.current && (fileInputRef.current.placeholder = 'https://example.com/file.png')
34
+ // }, []);
35
+
36
+ return (
37
+ <div style={{flexGrow: "1", display: "flex", height: "100%"}}>
38
+ <ControlGroup fill={false} vertical={false} style={{width: "100%"}}>
39
+ {!state.readOnly ? (<HTMLSelect options={['url', 'file']} minimal={true} fill={false}
40
+ disabled={state.disabled}
41
+ value={state.mode} onChange={(e)=>{
42
+ onChange({mode: e.target.value as 'url'|'file'})
43
+ }} /> ) : null}
44
+ {state.mode === 'url' ? (
45
+ <InputGroup style={{flexGrow: "1", flexShrink: "1"}} inputRef={textInputRef as any} type="url" defaultValue={typeof state.value === 'string' ? state.value : ''}
46
+ rightElement={(
47
+ <Button disabled={state.readOnly} minimal={true} icon="arrow-right" onClick={()=>{
48
+ onChange({mode:'url', value: textInputRef.current?.value??''})
49
+ }} ></Button>
50
+ )}
51
+ disabled={state.disabled} readOnly={state.readOnly}
52
+ onChange={(_)=>{
53
+ }} placeholder="https://example.com/file.png"/>
54
+ ) : (
55
+ <FileInput inputProps={{ref: fileInputRef as any}} style={{flexGrow: "1", flexShrink: "1", minWidth: "50px"}}
56
+ text={(state.value as File)?.name || 'No file'}
57
+ disabled={state.disabled}
58
+ onClick={(e)=>{
59
+ if(state.readOnly) {
60
+ e.preventDefault()
61
+ e.stopPropagation()
62
+ }
63
+ }}
64
+ buttonText={state.readOnly ? '-' : state.value ? 'Change' : 'Choose'}
65
+ onChange={()=>{
66
+ onChange({mode: 'file', value: fileInputRef.current?.files?.[0]??null})
67
+ }} className="inputGroupFile" />
68
+ )}
69
+ {!state.readOnly && state.value && (
70
+ <Button minimal={true} icon="small-cross"
71
+ disabled={state.disabled}
72
+ onClick={()=>{
73
+ onChange({value: null})
74
+ }}></Button>
75
+ )}
76
+ </ControlGroup>
77
+ </div>
78
+ )
79
+ }
@@ -0,0 +1,22 @@
1
+ import {FormGroup} from "@blueprintjs/core";
2
+ import React, {PropsWithChildren} from "react";
3
+
4
+ export function FormGroupComponent(props: PropsWithChildren<{ label: string, flexBasis?: string, disabled?: boolean }>) {
5
+ return (
6
+ <FormGroup className="xPaddedContent folderContent" contentClassName="formGroupContent"
7
+ style={{
8
+ justifyContent: "space-between",
9
+ marginTop: "2px",
10
+ marginBottom: "2px",
11
+ flexBasis: props.flexBasis ?? "50%"
12
+ }}
13
+ // helperText="Helper text with details..."
14
+ label={(<span title={props.label}>{props.label}</span>)}
15
+ inline={true}
16
+ disabled={props.disabled}
17
+ // labelInfo="(required)"
18
+ >
19
+ {props.children}
20
+ </FormGroup>
21
+ )
22
+ }
@@ -0,0 +1,72 @@
1
+ import React from "react";
2
+ import {Button, Card, Panel, PanelStack2, Popover} from "@blueprintjs/core";
3
+ import {ConfigObject, ConfigProps} from "../ConfigObject";
4
+ import {UiObjectConfig} from 'uiconfig.js'
5
+ import {UiConfigRendererContext} from '../bpComponents/BPComponent'
6
+ import {Classes} from '@blueprintjs/core/src/common'
7
+ import {defaultVisualStyle, ThemeSettingsMenuComponent, VisualStyleContext} from './VisualStyleContext'
8
+
9
+ // import {ViewerAppContext} from "./ViewerAppComponent";
10
+
11
+ export function InspectorStackComponent({config}: { config: UiObjectConfig<any, 'panel'> }) {
12
+ const renderer = React.useContext(UiConfigRendererContext)
13
+ const stackItem = React.useCallback(() => {
14
+ return {
15
+ props: {config: config},
16
+ renderPanel(props: ConfigProps) {
17
+ // console.log(props.config === config)
18
+ return (
19
+ <ul style={{listStyleType: "none", paddingLeft: "0", margin: "0"}}>
20
+ {/*{v && (<ConfigObject {...props}/>)}*/}
21
+ {config && (<ConfigObject {...props} isPanel={true}/>)}
22
+ </ul>
23
+ )
24
+ },
25
+ title: renderer.methods.getLabel(config)
26
+ } as Panel<{ config: UiObjectConfig }>
27
+ }, [config])
28
+ const [currentPanelStack, setCurrentPanelStack] = React.useState<Array<Panel<{ config: UiObjectConfig }>>>([stackItem()]);
29
+ React.useEffect(() => {
30
+ // console.warn('change', config)
31
+ setCurrentPanelStack([stackItem()])
32
+ }, [config])
33
+
34
+ const localStoragePrefix = 'bpUi'
35
+ const localStorageIds = {
36
+ 'darkMode': localStoragePrefix + 'DarkMode',
37
+ 'theme': localStoragePrefix + 'Theme',
38
+ }
39
+ const [darkMode, setDarkMode] = React.useState(localStorage.getItem(localStorageIds.darkMode) !== (defaultVisualStyle.darkMode ? 'false' : 'true'))
40
+ React.useEffect(() => {
41
+ localStorage.setItem('bpUiDarkMode', darkMode ? 'true' : 'false')
42
+ }, [darkMode])
43
+ const [theme, setTheme] = React.useState(localStorage.getItem(localStorageIds.theme) || defaultVisualStyle.theme)
44
+ React.useEffect(() => {
45
+ localStorage.setItem('bpUiTheme', theme)
46
+ document.documentElement.classList.remove(...Object.keys(defaultVisualStyle.themes).map(k => k))
47
+ document.documentElement.classList.add(theme)
48
+ }, [theme])
49
+
50
+ return (
51
+ <VisualStyleContext.Provider value={{...defaultVisualStyle, darkMode, setDarkMode, theme, setTheme}}>
52
+ <Card id="bpInspectorCard" className={darkMode?Classes.DARK:""} style={{height: "100%", flexGrow: "1", position: "relative"}}>
53
+ <PanelStack2 className="inspectorPanelStack"
54
+ showPanelHeader={true}
55
+ renderActivePanelOnly={true}
56
+ onOpen={(p) => setCurrentPanelStack([...currentPanelStack, p] as any)}
57
+ onClose={() => setCurrentPanelStack(currentPanelStack.slice(0, -1))}
58
+ stack={currentPanelStack}/>
59
+ <div className={"bpPanelStackHeaderOverlay " + Classes.PANEL_STACK2_HEADER}>
60
+ <Popover targetProps={{style: {position: "absolute", right: "5px"}}}
61
+ minimal={true}
62
+ targetTagName={"div"}
63
+ content={
64
+ <ThemeSettingsMenuComponent/>
65
+ } placement="bottom">
66
+ <Button icon="cog" small minimal text="" />
67
+ </Popover>
68
+ </div>
69
+ </Card>
70
+ </VisualStyleContext.Provider>
71
+ )
72
+ }
@@ -0,0 +1,69 @@
1
+ import React from 'react'
2
+ import {Menu, MenuItem} from '@blueprintjs/core'
3
+ import {Classes} from '@blueprintjs/core/src/common'
4
+
5
+ export const defaultVisualStyle = {
6
+ darkMode: true,
7
+ setDarkMode: (_: boolean) => {},
8
+ themes: {
9
+ // https://github.com/pnnl/blueprint-styler/blob/9a2f5461f6e8682f829bd9e1722c5c4c051f7c0f/src/styles/style-manifest.ts#L12
10
+ 'bpx-default': { label: 'Blueprint Default', link: 'https://blueprintjs.com/docs/#blueprint' },
11
+ 'bpx-bpv3': { label: 'Blueprint v3', link: 'https://blueprintjs.com/docs/versions/3/' },
12
+ 'bpx-flat': { label: 'Flat', link: 'https://pnnl.github.io/blueprint-styler/?style=bpx-flat' },
13
+ 'bpx-carbon': { label: 'IBM Carbon', link: 'https://carbondesignsystem.com/components/overview/' },
14
+ 'bpx-antd': { label: 'Ant Design', link: 'https://ant.design/components/overview/' },
15
+ 'bpx-fluent': { label: 'Microsoft Fluent', link: 'https://fluentuipr.z22.web.core.windows.net/heads/master/theming-designer/index.html' },
16
+ 'bpx-pnnl': { label: 'PNNL v3', link: 'https://forgedev.pnnl.gov/prc3/' },
17
+ // 'bpx-static': { label: 'None', link: 'https://blueprintjs.com/docs/#blueprint' },
18
+ },
19
+ theme: 'bpx-default',
20
+ setTheme: (_: string) => {},
21
+ }
22
+ export const VisualStyleContext = React.createContext<typeof defaultVisualStyle>(defaultVisualStyle)
23
+ export const useVisualStyle = () => React.useContext(VisualStyleContext)
24
+
25
+ export function ThemeSettingsMenuComponent() {
26
+ const visualStyle = useVisualStyle()
27
+ return <Menu className={Classes.ELEVATION_0} >
28
+ <MenuItem
29
+ icon="contrast"
30
+ text="Color"
31
+ // intent={intent}
32
+ // labelElement={"⌘,"}
33
+ roleStructure="menuitem"
34
+ children={
35
+ <>
36
+ <MenuItem icon="flash" text="Light Mode"
37
+ shouldDismissPopover={false}
38
+ selected={!visualStyle.darkMode}
39
+ onClick={() => visualStyle.setDarkMode(false)}
40
+ roleStructure="listoption"/>
41
+ <MenuItem icon="moon" text="Dark Mode"
42
+ shouldDismissPopover={false}
43
+ selected={visualStyle.darkMode}
44
+ onClick={() => visualStyle.setDarkMode(true)}
45
+ roleStructure="listoption"/>
46
+ </>
47
+ }
48
+ />
49
+ <MenuItem
50
+ icon="style"
51
+ text="Theme"
52
+ // intent={intent}
53
+ // labelElement={"⌘,"}
54
+ roleStructure="menuitem"
55
+ children={
56
+ <>
57
+ {Object.entries(visualStyle.themes).map(([key, value]) => (
58
+ <MenuItem
59
+ shouldDismissPopover={false}
60
+ text={value.label}
61
+ selected={visualStyle.theme === key}
62
+ onClick={() => visualStyle.setTheme(key)}
63
+ roleStructure="listoption"/>
64
+ ))}
65
+ </>
66
+ }
67
+ />
68
+ </Menu>
69
+ }
@@ -0,0 +1,20 @@
1
+ // https://github.com/palantir/blueprint/issues/2348#issuecomment-389322440
2
+ import React, {ReactNode} from "react";
3
+
4
+ export const bpIconWrapper = (path: ReactNode, viewbox = 16) => {
5
+ return (
6
+ <span className="bp5-icon">
7
+ <svg
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ x="0"
10
+ y="0"
11
+ width="16"
12
+ height="16"
13
+ role="img"
14
+ viewBox={`0 0 ${viewbox} ${viewbox}`}
15
+ >
16
+ {path}
17
+ </svg>
18
+ </span>
19
+ );
20
+ };
@@ -0,0 +1,21 @@
1
+
2
+ .bpButton {
3
+ display: flex;
4
+ justify-content: space-between;
5
+ }
6
+
7
+ .bpIconButton {
8
+ }
9
+
10
+ .bpPanelStackHeaderOverlay{
11
+ position: absolute;
12
+ top: 0;
13
+ left: 0;
14
+ width: 100%;
15
+ background: transparent;
16
+ box-shadow: none;
17
+ }
18
+
19
+ .hidden{
20
+ display: none;
21
+ }