uiconfig-blueprint 0.1.0-dev.13 → 0.1.0-dev.14
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/ConfigObject.d.ts +3 -1
- package/dist/{allPaths-Dbwyd6G0.js → allPaths-DANRR6MZ.js} +5 -5
- package/dist/{allPaths-Dbwyd6G0.js.map → allPaths-DANRR6MZ.js.map} +1 -1
- package/dist/{allPathsLoader-B9DtnPQs.js → allPathsLoader-BdHGVD6B.js} +4 -4
- package/dist/{allPathsLoader-B9DtnPQs.js.map → allPathsLoader-BdHGVD6B.js.map} +1 -1
- package/dist/bpComponents/BPColorInputComponent.d.ts +1 -0
- package/dist/bpComponents/BPComponent.d.ts +2 -0
- package/dist/bpComponents/BPContainerComponent.d.ts +10 -2
- package/dist/bpComponents/BPFileComponent.d.ts +12 -10
- package/dist/bpComponents/BPFolderComponent.d.ts +13 -4
- package/dist/bpComponents/BPPanelComponent.d.ts +3 -3
- package/dist/bpComponents/BPTextInputComponent.d.ts +4 -0
- package/dist/bpComponents/BPValueComponent.d.ts +2 -2
- package/dist/bpComponents/BPVectorInputComponent.d.ts +11 -5
- package/dist/bpComponents/InputGroup2.d.ts +25 -0
- package/dist/components/DialogContext.d.ts +10 -3
- package/dist/components/DraggableIcon.d.ts +4 -3
- package/dist/components/FileComponent.d.ts +13 -6
- package/dist/components/FormGroupComponent.d.ts +3 -2
- package/dist/components/InspectorStackComponent.d.ts +11 -2
- package/dist/index-6kveHKKU.js +695 -0
- package/dist/index-6kveHKKU.js.map +1 -0
- package/dist/index-D9WNL8Mc.js +695 -0
- package/dist/index-D9WNL8Mc.js.map +1 -0
- package/dist/index-t3WzvX5M.js +13501 -0
- package/dist/index-t3WzvX5M.js.map +1 -0
- package/dist/index.js +28 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/lib.d.ts +2 -3
- package/dist/{splitPathsBySizeLoader-Dii-Xb62.js → splitPathsBySizeLoader-Bhwx_O-M.js} +5 -5
- package/dist/{splitPathsBySizeLoader-Dii-Xb62.js.map → splitPathsBySizeLoader-Bhwx_O-M.js.map} +1 -1
- package/dist/utils/pointerLock.d.ts +44 -0
- package/docs/classes/UI.html +3 -3
- package/docs/classes/UiConfigRendererBlueprint.html +3 -3
- package/lib/css/renderer.css +35 -103
- package/lib/css/renderer.css.map +1 -1
- package/lib/esm/ConfigObject.d.ts +3 -1
- package/lib/esm/ConfigObject.js +5 -4
- package/lib/esm/ConfigObject.js.map +1 -1
- package/lib/esm/UiConfigRendererBlueprint.js +2 -1
- package/lib/esm/UiConfigRendererBlueprint.js.map +1 -1
- package/lib/esm/bpComponents/BPButtonComponent.js +1 -1
- package/lib/esm/bpComponents/BPButtonComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPColorInputComponent.d.ts +1 -0
- package/lib/esm/bpComponents/BPColorInputComponent.js +17 -4
- package/lib/esm/bpComponents/BPColorInputComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPComponent.d.ts +2 -0
- package/lib/esm/bpComponents/BPComponent.js +4 -4
- package/lib/esm/bpComponents/BPComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPContainerComponent.d.ts +10 -2
- package/lib/esm/bpComponents/BPContainerComponent.js +4 -1
- package/lib/esm/bpComponents/BPContainerComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPDropdownInputComponent.js +4 -3
- package/lib/esm/bpComponents/BPDropdownInputComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPFileComponent.d.ts +12 -10
- package/lib/esm/bpComponents/BPFileComponent.js +10 -8
- package/lib/esm/bpComponents/BPFileComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPFolderComponent.d.ts +13 -4
- package/lib/esm/bpComponents/BPFolderComponent.js +58 -13
- package/lib/esm/bpComponents/BPFolderComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPInputComponent.js +1 -1
- package/lib/esm/bpComponents/BPInputComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPPanelComponent.d.ts +3 -3
- package/lib/esm/bpComponents/BPPanelComponent.js +1 -1
- package/lib/esm/bpComponents/BPPanelComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPTextInputComponent.d.ts +4 -0
- package/lib/esm/bpComponents/BPTextInputComponent.js +34 -3
- package/lib/esm/bpComponents/BPTextInputComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPTreeComponent.d.ts +4 -4
- package/lib/esm/bpComponents/BPTreeComponent.js +2 -2
- package/lib/esm/bpComponents/BPTreeComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPValueComponent.d.ts +2 -2
- package/lib/esm/bpComponents/BPValueComponent.js +5 -4
- package/lib/esm/bpComponents/BPValueComponent.js.map +1 -1
- package/lib/esm/bpComponents/BPVectorInputComponent.d.ts +11 -5
- package/lib/esm/bpComponents/BPVectorInputComponent.js +47 -6
- package/lib/esm/bpComponents/BPVectorInputComponent.js.map +1 -1
- package/lib/esm/bpComponents/InputGroup2.d.ts +25 -0
- package/lib/esm/bpComponents/InputGroup2.js +93 -0
- package/lib/esm/bpComponents/InputGroup2.js.map +1 -0
- package/lib/esm/components/ConfigurationPanelComponent.js +1 -1
- package/lib/esm/components/ConfigurationPanelComponent.js.map +1 -1
- package/lib/esm/components/DialogContext.d.ts +10 -3
- package/lib/esm/components/DialogContext.js +14 -13
- package/lib/esm/components/DialogContext.js.map +1 -1
- package/lib/esm/components/DraggableIcon.d.ts +4 -3
- package/lib/esm/components/DraggableIcon.js +41 -39
- package/lib/esm/components/DraggableIcon.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/FileComponent.d.ts +13 -6
- package/lib/esm/components/FileComponent.js +15 -6
- package/lib/esm/components/FileComponent.js.map +1 -1
- package/lib/esm/components/FormGroupComponent.d.ts +3 -2
- package/lib/esm/components/FormGroupComponent.js +1 -6
- package/lib/esm/components/FormGroupComponent.js.map +1 -1
- package/lib/esm/components/InspectorStackComponent.d.ts +11 -2
- package/lib/esm/components/InspectorStackComponent.js +14 -4
- package/lib/esm/components/InspectorStackComponent.js.map +1 -1
- package/lib/esm/components/NumericInput.js +3 -2
- package/lib/esm/components/NumericInput.js.map +1 -1
- package/lib/esm/components/TreeNode2.d.ts +1 -1
- package/lib/esm/components/TreeNode2.js +4 -2
- package/lib/esm/components/TreeNode2.js.map +1 -1
- package/lib/esm/components/treeTypes.d.ts +2 -1
- package/lib/esm/lib.d.ts +2 -3
- package/lib/esm/lib.js +2 -3
- package/lib/esm/lib.js.map +1 -1
- package/lib/esm/renderer.css.js +1 -1
- package/lib/esm/utils/pointerLock.d.ts +44 -0
- package/lib/esm/utils/pointerLock.js +215 -0
- package/lib/esm/utils/pointerLock.js.map +1 -0
- package/lib/esm/utils.js +6 -1
- package/lib/esm/utils.js.map +1 -1
- package/package.json +10 -12
- package/src/ConfigObject.tsx +11 -5
- package/src/UiConfigRendererBlueprint.tsx +1 -1
- package/src/bpComponents/BPButtonComponent.tsx +1 -1
- package/src/bpComponents/BPColorInputComponent.tsx +13 -4
- package/src/bpComponents/BPComponent.tsx +4 -3
- package/src/bpComponents/BPContainerComponent.tsx +20 -3
- package/src/bpComponents/BPDropdownInputComponent.tsx +4 -3
- package/src/bpComponents/BPFileComponent.tsx +29 -20
- package/src/bpComponents/BPFolderComponent.scss +11 -6
- package/src/bpComponents/BPFolderComponent.tsx +122 -24
- package/src/bpComponents/BPInputComponent.tsx +1 -0
- package/src/bpComponents/BPPanelComponent.tsx +4 -4
- package/src/bpComponents/BPTextInputComponent.tsx +38 -5
- package/src/bpComponents/BPValueComponent.tsx +4 -4
- package/src/bpComponents/BPVectorInputComponent.tsx +80 -10
- package/src/bpComponents/InputGroup2.tsx +175 -0
- package/src/bpComponents/index.scss +0 -1
- package/src/components/ConfigurationPanelComponent.tsx +2 -2
- package/src/components/DialogContext.tsx +20 -12
- package/src/components/DraggableIcon.tsx +71 -49
- package/src/components/ExtendedNumericInput.tsx +2 -2
- package/src/components/FileComponent.tsx +37 -18
- package/src/components/FormGroupComponent.tsx +8 -4
- package/src/components/InspectorStackComponent.tsx +20 -7
- package/src/components/NumericInput.tsx +4 -5
- package/src/components/common.scss +1 -1
- package/src/lib.ts +2 -3
- package/src/renderer.scss +30 -8
- package/src/utils/PointerLockManager.md +334 -0
- package/src/utils/pointerLock.ts +264 -0
- package/src/utils.ts +8 -4
- package/dist/bpComponents/BPTreeComponent.d.ts +0 -46
- package/dist/bpComponents/BPTreeFolderComponent.d.ts +0 -10
- package/dist/components/Tree2.d.ts +0 -52
- package/dist/components/TreeNode2.d.ts +0 -43
- package/dist/components/treeTypes.d.ts +0 -18
- package/dist/index-BFBvfUPU.js +0 -671
- package/dist/index-BFBvfUPU.js.map +0 -1
- package/dist/index-CHQ1fP9-.js +0 -671
- package/dist/index-CHQ1fP9-.js.map +0 -1
- package/dist/index-Dbe8gLld.js +0 -13583
- package/dist/index-Dbe8gLld.js.map +0 -1
- package/src/bpComponents/BPTreeComponent.scss +0 -3
- package/src/bpComponents/BPTreeComponent.tsx +0 -244
- package/src/bpComponents/BPTreeFolderComponent.tsx +0 -31
- package/src/components/Tree2.tsx +0 -344
- package/src/components/TreeNode2.tsx +0 -192
- package/src/components/tree2.scss +0 -69
- package/src/components/treeTypes.ts +0 -97
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
import React, {useEffect, useRef} from "react";
|
|
1
|
+
import React, {JSX, ReactNode, useEffect, useRef} from "react";
|
|
2
2
|
import {Button, ControlGroup, FileInput, HTMLSelect, InputGroup} from "@blueprintjs/core";
|
|
3
3
|
import {BPFileComponentState} from "../bpComponents/BPFileComponent";
|
|
4
4
|
import {useLoadingStateKey} from './loadingState'
|
|
5
5
|
|
|
6
|
-
export interface FileComponentProps {
|
|
7
|
-
state: BPFileComponentState
|
|
8
|
-
onChange: (state: Partial<BPFileComponentState
|
|
9
|
-
previewSlot?:
|
|
6
|
+
export interface FileComponentProps<TSV = never> {
|
|
7
|
+
state: BPFileComponentState<TSV>
|
|
8
|
+
onChange: (state: Partial<BPFileComponentState<TSV>>)=>Promise<void>
|
|
9
|
+
previewSlot?: ReactNode,
|
|
10
10
|
uuid?: string
|
|
11
|
+
AssetPicker?: (props: {
|
|
12
|
+
state: BPFileComponentState<TSV>
|
|
13
|
+
onChange: (state: Partial<BPFileComponentState<TSV>>)=>Promise<void>
|
|
14
|
+
className?: string
|
|
15
|
+
style?: React.CSSProperties
|
|
16
|
+
})=>JSX.Element
|
|
17
|
+
disableFileInput?: boolean // todo
|
|
11
18
|
}
|
|
12
|
-
export const FileComponent
|
|
19
|
+
export const FileComponent = <TSV,>({state, onChange, previewSlot, AssetPicker}: FileComponentProps<TSV>)=>{
|
|
13
20
|
const fileInputRef = useRef<HTMLInputElement>()
|
|
14
21
|
const textInputRef = useRef<HTMLInputElement>()
|
|
15
22
|
// console.log('render file component', state)
|
|
@@ -28,8 +35,15 @@ export const FileComponent: React.FC<FileComponentProps> = ({state, onChange, pr
|
|
|
28
35
|
}
|
|
29
36
|
if(!fileElement) return;
|
|
30
37
|
// console.log(val)
|
|
38
|
+
|
|
39
|
+
// it can be Blob also because of GLTF loaded...
|
|
40
|
+
const file = val instanceof File ? val :
|
|
41
|
+
typeof val === 'string' || typeof (val as any as Blob).arrayBuffer === 'function' ?
|
|
42
|
+
new File([val as string | Blob], (val as any).name||('file'+((val as any).ext?'.'+(val as any).ext:'')))
|
|
43
|
+
: null
|
|
44
|
+
if(!file) return
|
|
31
45
|
const dataTransfer = new DataTransfer();
|
|
32
|
-
dataTransfer.items.add(
|
|
46
|
+
dataTransfer.items.add(file);
|
|
33
47
|
fileElement!.files = dataTransfer.files
|
|
34
48
|
}, [state.value]);
|
|
35
49
|
// useEffect(() => {
|
|
@@ -38,6 +52,9 @@ export const FileComponent: React.FC<FileComponentProps> = ({state, onChange, pr
|
|
|
38
52
|
|
|
39
53
|
// const hasValue = !!state.value || !!state.preview
|
|
40
54
|
|
|
55
|
+
const dropdownOptions = ['url', 'file']
|
|
56
|
+
if(AssetPicker) dropdownOptions.push('asset')
|
|
57
|
+
|
|
41
58
|
const {loadingState, updateLoading} = useLoadingStateKey()
|
|
42
59
|
|
|
43
60
|
return (
|
|
@@ -46,8 +63,8 @@ export const FileComponent: React.FC<FileComponentProps> = ({state, onChange, pr
|
|
|
46
63
|
<>
|
|
47
64
|
{previewSlot}
|
|
48
65
|
<Button icon="small-cross"
|
|
49
|
-
|
|
50
|
-
|
|
66
|
+
size={"small"}
|
|
67
|
+
variant={"solid"}
|
|
51
68
|
disabled={state.disabled}
|
|
52
69
|
intent={"danger"}
|
|
53
70
|
style={{position: "absolute", top: 0, right: 0, opacity: 0.7}}
|
|
@@ -55,25 +72,25 @@ export const FileComponent: React.FC<FileComponentProps> = ({state, onChange, pr
|
|
|
55
72
|
onClick={() => updateLoading(onChange({value: null}))}></Button>
|
|
56
73
|
</>
|
|
57
74
|
)}
|
|
58
|
-
|
|
59
|
-
{!state.readOnly && !state.value ? (<HTMLSelect options={
|
|
75
|
+
<ControlGroup fill={false} vertical={false} style={{width: "100%"}}>
|
|
76
|
+
{!state.readOnly && !state.value ? (<HTMLSelect options={dropdownOptions} minimal={true} fill={false}
|
|
60
77
|
disabled={state.disabled}
|
|
61
78
|
className={"file-component-select"}
|
|
62
79
|
value={state.mode} onChange={(e) => {
|
|
63
|
-
onChange({mode: e.target.value as '
|
|
80
|
+
onChange({mode: e.target.value as BPFileComponentState['mode']})
|
|
64
81
|
}}/>) : null}
|
|
65
82
|
{state.mode === 'url' ? (
|
|
66
83
|
<InputGroup style={{flexGrow: "1", flexShrink: "1"}} inputRef={textInputRef as any} type="url"
|
|
67
84
|
defaultValue={typeof state.value === 'string' ? state.value : ''}
|
|
68
85
|
rightElement={(
|
|
69
|
-
<Button disabled={state.readOnly}
|
|
86
|
+
<Button disabled={state.readOnly} variant={"minimal"} icon="arrow-right" onClick={() => {
|
|
70
87
|
onChange({mode: 'url', value: textInputRef.current?.value ?? ''})
|
|
71
88
|
}}></Button>
|
|
72
89
|
)}
|
|
73
90
|
disabled={state.disabled} readOnly={state.readOnly}
|
|
74
91
|
onChange={(_) => {
|
|
75
92
|
}} placeholder="https://example.com/file.png"/>
|
|
76
|
-
) : (
|
|
93
|
+
) : state.mode === 'file' ? (
|
|
77
94
|
<FileInput inputProps={{ref: fileInputRef as any}}
|
|
78
95
|
style={{flexGrow: "1", flexShrink: "1", minWidth: "50px"}}
|
|
79
96
|
text={(state.value as File)?.name || 'No file'}
|
|
@@ -88,15 +105,17 @@ export const FileComponent: React.FC<FileComponentProps> = ({state, onChange, pr
|
|
|
88
105
|
onChange={() => {
|
|
89
106
|
onChange({mode: 'file', value: fileInputRef.current?.files?.[0] ?? null})
|
|
90
107
|
}} className="inputGroupFile"/>
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
) : state.mode === 'asset' && AssetPicker ? (
|
|
109
|
+
<AssetPicker state={state} onChange={onChange} className="inputGroupFile"/>
|
|
110
|
+
) : null}
|
|
111
|
+
{!state.readOnly && state.value && !state.preview && (
|
|
112
|
+
<Button variant={"minimal"} icon="small-cross"
|
|
94
113
|
disabled={state.disabled}
|
|
95
114
|
loading={loadingState}
|
|
96
115
|
onClick={() => updateLoading(onChange({value: null}))
|
|
97
116
|
}></Button>
|
|
98
117
|
)}
|
|
99
|
-
</ControlGroup>
|
|
118
|
+
</ControlGroup>
|
|
100
119
|
</div>
|
|
101
120
|
)
|
|
102
121
|
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import {FormGroup} from "@blueprintjs/core";
|
|
2
|
-
import {PropsWithChildren} from "react";
|
|
2
|
+
import {CSSProperties, PropsWithChildren, ReactNode} from "react";
|
|
3
3
|
|
|
4
|
-
export function FormGroupComponent(props: PropsWithChildren<{
|
|
4
|
+
export function FormGroupComponent(props: PropsWithChildren<{
|
|
5
|
+
label: string|ReactNode, flexBasis?: string, disabled?: boolean,
|
|
6
|
+
style?: CSSProperties
|
|
7
|
+
}>) {
|
|
5
8
|
return (
|
|
6
9
|
<FormGroup className="xPaddedContent folderContent" contentClassName="formGroupContent"
|
|
7
10
|
style={{
|
|
8
11
|
justifyContent: "space-between",
|
|
9
12
|
marginTop: "2px",
|
|
10
13
|
marginBottom: "2px",
|
|
11
|
-
flexBasis: props.flexBasis ?? "50%"
|
|
14
|
+
flexBasis: props.flexBasis ?? "50%",
|
|
15
|
+
...props.style,
|
|
12
16
|
}}
|
|
13
17
|
// helperText="Helper text with details..."
|
|
14
|
-
label={(<span title={props.label}>{props.label}</span>)}
|
|
18
|
+
label={typeof props.label !== 'string' ? props.label : (<span title={props.label}>{props.label}</span>)}
|
|
15
19
|
inline={true}
|
|
16
20
|
disabled={props.disabled}
|
|
17
21
|
// labelInfo="(required)"
|
|
@@ -2,12 +2,14 @@ import React from "react";
|
|
|
2
2
|
import {Card, Panel, PanelStack2} from "@blueprintjs/core";
|
|
3
3
|
import {ConfigObject, ConfigProps} from "../ConfigObject";
|
|
4
4
|
import {UiObjectConfig} from 'uiconfig.js'
|
|
5
|
-
import {UiConfigRendererContext} from '../bpComponents/BPComponent'
|
|
5
|
+
import {UiConfigRendererBaseBp, UiConfigRendererContext} from '../bpComponents/BPComponent'
|
|
6
|
+
import {getOrCall, ValOrFunc} from "ts-browser-helpers";
|
|
6
7
|
|
|
7
8
|
// import {ViewerAppContext} from "./ViewerAppComponent";
|
|
8
9
|
|
|
9
|
-
export function
|
|
10
|
-
|
|
10
|
+
export function useConfigToStackItem(config: UiObjectConfig<any, 'panel'>, renderer?: UiConfigRendererBaseBp){
|
|
11
|
+
renderer = renderer ?? React.useContext(UiConfigRendererContext)
|
|
12
|
+
if(!renderer) throw new Error('No renderer provided or found in context')
|
|
11
13
|
const stackItem = React.useCallback(() => {
|
|
12
14
|
return {
|
|
13
15
|
props: {config: config},
|
|
@@ -20,13 +22,24 @@ export function InspectorStackComponent({config, className}: { config: UiObjectC
|
|
|
20
22
|
</ul>
|
|
21
23
|
)
|
|
22
24
|
},
|
|
23
|
-
title: renderer
|
|
25
|
+
title: renderer!.methods.getLabel(config)
|
|
24
26
|
} as Panel<{ config: UiObjectConfig }>
|
|
25
27
|
}, [config])
|
|
26
|
-
|
|
28
|
+
return stackItem
|
|
29
|
+
}
|
|
30
|
+
export type InspectorStackItem = ValOrFunc<Panel<{ config: UiObjectConfig }>>
|
|
31
|
+
function getStackItem(i: InspectorStackItem){
|
|
32
|
+
const r = getOrCall(i)
|
|
33
|
+
return r?[r] : []
|
|
34
|
+
}
|
|
35
|
+
export function InspectorStackComponent({stackItem, className}: {
|
|
36
|
+
stackItem: InspectorStackItem,
|
|
37
|
+
className?: string
|
|
38
|
+
}) {
|
|
39
|
+
const [currentPanelStack, setCurrentPanelStack] = React.useState<Array<Panel<{ config: UiObjectConfig }>>>(getStackItem(stackItem));
|
|
27
40
|
React.useEffect(() => {
|
|
28
|
-
// console.warn('change',
|
|
29
|
-
setCurrentPanelStack(
|
|
41
|
+
// console.warn('change', stackItem)
|
|
42
|
+
setCurrentPanelStack(getStackItem(stackItem))
|
|
30
43
|
}, [setCurrentPanelStack, stackItem])
|
|
31
44
|
|
|
32
45
|
return (
|
|
@@ -27,7 +27,6 @@ import {
|
|
|
27
27
|
ControlGroup,
|
|
28
28
|
DISPLAYNAME_PREFIX,
|
|
29
29
|
type HTMLInputProps,
|
|
30
|
-
InputGroup,
|
|
31
30
|
Intent,
|
|
32
31
|
Position,
|
|
33
32
|
refHandler,
|
|
@@ -46,6 +45,7 @@ import {
|
|
|
46
45
|
sanitizeNumericInput,
|
|
47
46
|
toLocaleString,
|
|
48
47
|
} from "@blueprintjs/core/src/components/forms/numericInputUtils";
|
|
48
|
+
import {InputGroup2} from "../bpComponents/InputGroup2";
|
|
49
49
|
|
|
50
50
|
export interface NumericInputProps extends InputSharedProps {
|
|
51
51
|
/**
|
|
@@ -439,8 +439,8 @@ export class NumericInput extends AbstractPureComponent<HTMLInputProps & Numeric
|
|
|
439
439
|
const valueAsNumber = this.getCurrentValueAsNumber();
|
|
440
440
|
|
|
441
441
|
return (
|
|
442
|
-
<
|
|
443
|
-
asyncControl={this.props.asyncControl}
|
|
442
|
+
<InputGroup2
|
|
443
|
+
// asyncControl={this.props.asyncControl}
|
|
444
444
|
autoComplete="off"
|
|
445
445
|
id={this.numericInputId}
|
|
446
446
|
role={this.props.allowNumericCharactersOnly ? "spinbutton" : undefined}
|
|
@@ -451,7 +451,6 @@ export class NumericInput extends AbstractPureComponent<HTMLInputProps & Numeric
|
|
|
451
451
|
intent={this.state.currentImeInputInvalid ? Intent.DANGER : this.props.intent}
|
|
452
452
|
inputClassName={this.props.inputClassName}
|
|
453
453
|
inputRef={this.inputRef}
|
|
454
|
-
large={this.props.large}
|
|
455
454
|
leftElement={this.props.leftElement}
|
|
456
455
|
leftIcon={this.props.leftIcon}
|
|
457
456
|
onFocus={this.handleInputFocus}
|
|
@@ -463,7 +462,7 @@ export class NumericInput extends AbstractPureComponent<HTMLInputProps & Numeric
|
|
|
463
462
|
onPaste={this.handleInputPaste}
|
|
464
463
|
onValueChange={this.handleInputChange}
|
|
465
464
|
rightElement={this.props.rightElement}
|
|
466
|
-
|
|
465
|
+
size={this.props.small ? "small" : this.props.large ? "large" : "medium"}
|
|
467
466
|
value={this.state.value}
|
|
468
467
|
/>
|
|
469
468
|
);
|
package/src/lib.ts
CHANGED
|
@@ -13,7 +13,7 @@ export * from './components/VisualStyleContext'
|
|
|
13
13
|
export * from './components/DialogContext'
|
|
14
14
|
export * from './components/loadingState'
|
|
15
15
|
export * from './components/AppToaster'
|
|
16
|
-
export * from
|
|
16
|
+
export * from './bpComponents/InputGroup2'
|
|
17
17
|
export * from './bpComponents/BPButtonComponent'
|
|
18
18
|
export * from './bpComponents/BPColorInputComponent'
|
|
19
19
|
export * from './bpComponents/BPComponent'
|
|
@@ -28,9 +28,8 @@ export * from './bpComponents/BPPanelComponent'
|
|
|
28
28
|
export * from './bpComponents/BPSliderInputComponent'
|
|
29
29
|
export * from './bpComponents/BPTextInputComponent'
|
|
30
30
|
export * from './bpComponents/BPToggleInputComponent'
|
|
31
|
-
export * from './bpComponents/BPTreeComponent'
|
|
32
|
-
export * from './bpComponents/BPTreeFolderComponent'
|
|
33
31
|
export * from './bpComponents/BPValueComponent'
|
|
34
32
|
export * from './bpComponents/BPVectorInputComponent'
|
|
35
33
|
export * from "./bpComponents/NumberUnits";
|
|
36
34
|
export * from "./bpComponents/GetNumberTransformFunctions";
|
|
35
|
+
export {InputGroup2} from "./bpComponents/InputGroup2";
|
package/src/renderer.scss
CHANGED
|
@@ -14,6 +14,15 @@
|
|
|
14
14
|
@import "blueprint-styler/base/blueprint";
|
|
15
15
|
@import "blueprint-styler/base/tokens.scss";
|
|
16
16
|
|
|
17
|
+
.bp5-vars, :root {
|
|
18
|
+
//--pt-icon-color: var(--pt-text-color);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.bp5-dark {
|
|
22
|
+
--pt-icon-color: var(--pt-text-color);
|
|
23
|
+
--menu-background-color: var(--tooltip-background-color);
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
:root.bpx-bpv3{
|
|
18
27
|
@import "blueprint-styler/overrides/bpv3/custom-tokens.scss";
|
|
19
28
|
@import "blueprint-styler/overrides/bpv3/override";
|
|
@@ -117,14 +126,14 @@
|
|
|
117
126
|
--background-color-transition-time: 0.2s;
|
|
118
127
|
--background-color-transition: background-color var(--background-color-transition-time) ease-in-out;
|
|
119
128
|
}
|
|
120
|
-
.bp5-panel-
|
|
129
|
+
.bp5-panel-stack2-view {
|
|
121
130
|
background-color: $light-gray4 !important;
|
|
122
131
|
transition: var(--background-color-transition);
|
|
123
132
|
}
|
|
124
133
|
|
|
125
134
|
.bp5-dark{
|
|
126
|
-
.bp5-panel-
|
|
127
|
-
background-color: $dark-
|
|
135
|
+
.bp5-panel-stack2-view {
|
|
136
|
+
background-color: $dark-gray1 !important;
|
|
128
137
|
}
|
|
129
138
|
}
|
|
130
139
|
|
|
@@ -232,10 +241,6 @@
|
|
|
232
241
|
}
|
|
233
242
|
}
|
|
234
243
|
|
|
235
|
-
.bp5-panel-stack2-view{
|
|
236
|
-
background: transparent;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
244
|
.bp5-slider-handle{
|
|
240
245
|
border-radius: 100%;
|
|
241
246
|
}
|
|
@@ -253,7 +258,7 @@
|
|
|
253
258
|
}
|
|
254
259
|
|
|
255
260
|
.bp5-dark {
|
|
256
|
-
--slider-progress-color: hsla(var(--gray4-hsl),0.15);
|
|
261
|
+
--slider-progress-color: hsla(var(--gray4-hsl), 0.15);
|
|
257
262
|
}
|
|
258
263
|
|
|
259
264
|
.bp5-slider-progress, .bp5-slider-track{
|
|
@@ -265,3 +270,20 @@
|
|
|
265
270
|
background-color: hsla(var(--pt-intent-primary-hsl), 1) !important;
|
|
266
271
|
//backdrop-filter: blur(4px);
|
|
267
272
|
}
|
|
273
|
+
|
|
274
|
+
.bp5-button.bp5-small {
|
|
275
|
+
.bp5-button-text{
|
|
276
|
+
font-size: $pt-font-size-small;
|
|
277
|
+
padding: calc($pt-grid-size * 0.5) 0 calc($pt-grid-size * 0.5) 0;
|
|
278
|
+
}
|
|
279
|
+
.bp5-icon > svg{
|
|
280
|
+
width: $pt-font-size;
|
|
281
|
+
height: $pt-font-size;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// todo not sure why its required again, its also in blueprint...
|
|
286
|
+
.bp5-dark .bp5-divider{
|
|
287
|
+
border-color:rgba(255, 255, 255, 0.2);
|
|
288
|
+
}
|
|
289
|
+
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# PointerLockManager
|
|
2
|
+
|
|
3
|
+
A utility class for implementing Unity-style cursor wrap-around behavior using the Pointer Lock API. This allows for infinite dragging without cursor constraints.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `PointerLockManager` provides a seamless way to capture the mouse pointer, hide the real cursor, display a fake cursor, and implement cursor wrap-around at screen edges. This is particularly useful for draggable UI elements that need unlimited range of motion.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🔒 **Pointer Lock API integration** - Captures mouse for unlimited movement
|
|
12
|
+
- 🎯 **Fake cursor rendering** - Shows a custom cursor while the real one is hidden
|
|
13
|
+
- 🔄 **Screen edge wrap-around** - Cursor automatically wraps to opposite edge
|
|
14
|
+
- 📍 **Movement tracking** - Provides both movement deltas and absolute cursor position
|
|
15
|
+
- 🧹 **Automatic cleanup** - Properly releases pointer lock and cleans up resources
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
The PointerLockManager is part of the internal utils package:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { PointerLockManager } from '../utils/pointerLock';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Basic Usage
|
|
26
|
+
|
|
27
|
+
### 1. Setup
|
|
28
|
+
|
|
29
|
+
Create a reference to the element that will request pointer lock:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import * as React from 'react';
|
|
33
|
+
import { PointerLockManager } from '../utils/pointerLock';
|
|
34
|
+
|
|
35
|
+
function MyComponent() {
|
|
36
|
+
const pointerLockManagerRef = React.useRef<PointerLockManager | null>(null);
|
|
37
|
+
const elementRef = React.useRef<HTMLDivElement>(null);
|
|
38
|
+
|
|
39
|
+
// ... rest of component
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Initialize on Mouse Down
|
|
44
|
+
|
|
45
|
+
Request pointer lock when the user starts interacting:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
const handleMouseDown = (ev: React.MouseEvent) => {
|
|
49
|
+
if (!elementRef.current) return;
|
|
50
|
+
|
|
51
|
+
// Create the pointer lock manager
|
|
52
|
+
pointerLockManagerRef.current = new PointerLockManager(
|
|
53
|
+
elementRef.current,
|
|
54
|
+
{
|
|
55
|
+
onMove: handlePointerMove,
|
|
56
|
+
onLockChange: (locked) => {
|
|
57
|
+
if (!locked) {
|
|
58
|
+
cleanup();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Request lock at current cursor position
|
|
65
|
+
pointerLockManagerRef.current.requestLock(ev.clientX, ev.clientY);
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 3. Handle Movement
|
|
70
|
+
|
|
71
|
+
Implement the movement handler to track cursor changes:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
const handlePointerMove = (
|
|
75
|
+
movementX: number,
|
|
76
|
+
movementY: number,
|
|
77
|
+
cursorX: number,
|
|
78
|
+
cursorY: number
|
|
79
|
+
) => {
|
|
80
|
+
// movementX, movementY: delta movement since last event
|
|
81
|
+
// cursorX, cursorY: absolute fake cursor position on screen
|
|
82
|
+
|
|
83
|
+
console.log(`Movement: ${movementX}, ${movementY}`);
|
|
84
|
+
console.log(`Cursor at: ${cursorX}, ${cursorY}`);
|
|
85
|
+
|
|
86
|
+
// Apply movement to your logic
|
|
87
|
+
updateValue(movementX);
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 4. Release Lock
|
|
92
|
+
|
|
93
|
+
Clean up when interaction ends:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
const cleanup = () => {
|
|
97
|
+
if (pointerLockManagerRef.current) {
|
|
98
|
+
pointerLockManagerRef.current.releaseLock();
|
|
99
|
+
pointerLockManagerRef.current = null;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Call cleanup on mouse up
|
|
104
|
+
onMouseUp={() => {
|
|
105
|
+
cleanup();
|
|
106
|
+
}}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Complete Example
|
|
110
|
+
|
|
111
|
+
Here's a complete example of a draggable number input:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import * as React from 'react';
|
|
115
|
+
import { PointerLockManager } from '../utils/pointerLock';
|
|
116
|
+
|
|
117
|
+
interface DraggableNumberInputProps {
|
|
118
|
+
value: number;
|
|
119
|
+
onChange: (value: number) => void;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function DraggableNumberInput({ value, onChange }: DraggableNumberInputProps) {
|
|
123
|
+
const pointerLockManagerRef = React.useRef<PointerLockManager | null>(null);
|
|
124
|
+
const divRef = React.useRef<HTMLDivElement>(null);
|
|
125
|
+
const dragStartValueRef = React.useRef(0);
|
|
126
|
+
const accumulatedOffsetRef = React.useRef(0);
|
|
127
|
+
|
|
128
|
+
const handlePointerMove = React.useCallback(
|
|
129
|
+
(movementX: number, _movementY: number, _cursorX: number, _cursorY: number) => {
|
|
130
|
+
// Accumulate horizontal movement
|
|
131
|
+
accumulatedOffsetRef.current += movementX;
|
|
132
|
+
|
|
133
|
+
// Calculate new value
|
|
134
|
+
const newValue = dragStartValueRef.current + accumulatedOffsetRef.current * 0.1;
|
|
135
|
+
|
|
136
|
+
// Update parent
|
|
137
|
+
onChange(newValue);
|
|
138
|
+
},
|
|
139
|
+
[onChange]
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const endDrag = React.useCallback(() => {
|
|
143
|
+
if (pointerLockManagerRef.current) {
|
|
144
|
+
pointerLockManagerRef.current.releaseLock();
|
|
145
|
+
pointerLockManagerRef.current = null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
document.documentElement.style.cursor = 'auto';
|
|
149
|
+
document.removeEventListener('mouseup', endDrag);
|
|
150
|
+
}, []);
|
|
151
|
+
|
|
152
|
+
const startDrag = React.useCallback(
|
|
153
|
+
(ev: React.MouseEvent) => {
|
|
154
|
+
if (!divRef.current) return;
|
|
155
|
+
|
|
156
|
+
dragStartValueRef.current = value;
|
|
157
|
+
accumulatedOffsetRef.current = 0;
|
|
158
|
+
|
|
159
|
+
// Hide real cursor
|
|
160
|
+
document.documentElement.style.cursor = 'none';
|
|
161
|
+
|
|
162
|
+
// Create pointer lock manager
|
|
163
|
+
pointerLockManagerRef.current = new PointerLockManager(divRef.current, {
|
|
164
|
+
onMove: handlePointerMove,
|
|
165
|
+
onLockChange: (locked) => {
|
|
166
|
+
if (!locked) {
|
|
167
|
+
endDrag();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
pointerLockManagerRef.current.requestLock(ev.clientX, ev.clientY);
|
|
173
|
+
|
|
174
|
+
this.pointerLockManager = null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
document.documentElement.style.cursor = 'auto';
|
|
178
|
+
document.removeEventListener('mouseup', this.endDrag);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
render() {
|
|
182
|
+
return (
|
|
183
|
+
<div
|
|
184
|
+
ref={this.divRef}
|
|
185
|
+
onMouseDown={this.startDrag}
|
|
186
|
+
style={{ cursor: 'ew-resize' }}
|
|
187
|
+
>
|
|
188
|
+
Value: {this.props.value.toFixed(2)}
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## API Reference
|
|
196
|
+
|
|
197
|
+
### Constructor
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
constructor(element: HTMLElement, options: PointerLockOptions)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Parameters:**
|
|
204
|
+
- `element`: The HTML element that will request pointer lock
|
|
205
|
+
- `options`: Configuration object
|
|
206
|
+
|
|
207
|
+
### PointerLockOptions
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
interface PointerLockOptions {
|
|
211
|
+
onMove: (movementX: number, movementY: number, cursorX: number, cursorY: number) => void;
|
|
212
|
+
onLockChange?: (locked: boolean) => void;
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Properties:**
|
|
217
|
+
- `onMove`: Callback fired on mouse movement
|
|
218
|
+
- `movementX`: Horizontal movement delta (pixels)
|
|
219
|
+
- `movementY`: Vertical movement delta (pixels)
|
|
220
|
+
- `cursorX`: Absolute X position of fake cursor (pixels)
|
|
221
|
+
- `cursorY`: Absolute Y position of fake cursor (pixels)
|
|
222
|
+
- `onLockChange`: Optional callback when lock state changes
|
|
223
|
+
- `locked`: Whether pointer is currently locked
|
|
224
|
+
|
|
225
|
+
### Methods
|
|
226
|
+
|
|
227
|
+
#### `requestLock(initialX: number, initialY: number): void`
|
|
228
|
+
|
|
229
|
+
Requests pointer lock and shows the fake cursor.
|
|
230
|
+
|
|
231
|
+
**Parameters:**
|
|
232
|
+
- `initialX`: Initial X position for fake cursor
|
|
233
|
+
- `initialY`: Initial Y position for fake cursor
|
|
234
|
+
|
|
235
|
+
**Example:**
|
|
236
|
+
```typescript
|
|
237
|
+
manager.requestLock(event.clientX, event.clientY);
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
#### `releaseLock(): void`
|
|
241
|
+
|
|
242
|
+
Releases pointer lock and hides the fake cursor. Automatically called when the user presses ESC.
|
|
243
|
+
|
|
244
|
+
**Example:**
|
|
245
|
+
```typescript
|
|
246
|
+
manager.releaseLock();
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### `isPointerLocked(): boolean`
|
|
250
|
+
|
|
251
|
+
Returns whether the pointer is currently locked.
|
|
252
|
+
|
|
253
|
+
**Returns:** `true` if locked, `false` otherwise
|
|
254
|
+
|
|
255
|
+
**Example:**
|
|
256
|
+
```typescript
|
|
257
|
+
if (manager.isPointerLocked()) {
|
|
258
|
+
console.log('Pointer is locked');
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Cursor Wrap-Around Behavior
|
|
263
|
+
|
|
264
|
+
When the fake cursor reaches the edge of the screen, it automatically wraps to the opposite side:
|
|
265
|
+
|
|
266
|
+
- **Left edge** → wraps to right edge
|
|
267
|
+
- **Right edge** → wraps to left edge
|
|
268
|
+
- **Top edge** → wraps to bottom edge
|
|
269
|
+
- **Bottom edge** → wraps to top edge
|
|
270
|
+
|
|
271
|
+
This provides infinite dragging space, similar to Unity's inspector fields.
|
|
272
|
+
|
|
273
|
+
## Fake Cursor
|
|
274
|
+
|
|
275
|
+
The fake cursor is a singleton instance shared across all `PointerLockManager` instances. It:
|
|
276
|
+
|
|
277
|
+
- Appears as a white arrow with black stroke
|
|
278
|
+
- Has `pointer-events: none` to not interfere with interactions
|
|
279
|
+
- Has maximum z-index (999999) to stay on top
|
|
280
|
+
- Is automatically managed (shown/hidden) by the manager
|
|
281
|
+
|
|
282
|
+
## Best Practices
|
|
283
|
+
|
|
284
|
+
1. **Always clean up**: Make sure to call `releaseLock()` when done
|
|
285
|
+
2. **Listen for ESC**: The browser will release pointer lock when ESC is pressed, handle this in `onLockChange`
|
|
286
|
+
3. **Store reference**: Keep the manager instance as a class property
|
|
287
|
+
4. **Reset state**: Reset your drag state when pointer lock is released
|
|
288
|
+
5. **Hide real cursor**: Set `cursor: 'none'` on a parent element when locking
|
|
289
|
+
|
|
290
|
+
## Browser Compatibility
|
|
291
|
+
|
|
292
|
+
The Pointer Lock API is supported in all modern browsers:
|
|
293
|
+
- Chrome/Edge 37+
|
|
294
|
+
- Firefox 50+
|
|
295
|
+
- Safari 10.1+
|
|
296
|
+
|
|
297
|
+
## Limitations
|
|
298
|
+
|
|
299
|
+
- User can exit pointer lock by pressing ESC
|
|
300
|
+
- Some browsers may show a notification when pointer lock is activated
|
|
301
|
+
- Pointer lock can only be requested in response to user interaction (e.g., click)
|
|
302
|
+
- Only one element can have pointer lock at a time
|
|
303
|
+
|
|
304
|
+
## Troubleshooting
|
|
305
|
+
|
|
306
|
+
### Pointer lock not activating
|
|
307
|
+
|
|
308
|
+
**Problem:** `requestLock()` is called but nothing happens.
|
|
309
|
+
|
|
310
|
+
**Solution:** Ensure the call is made in response to a user gesture (like a mouse click).
|
|
311
|
+
|
|
312
|
+
### Fake cursor not showing
|
|
313
|
+
|
|
314
|
+
**Problem:** Pointer locks but no cursor appears.
|
|
315
|
+
|
|
316
|
+
**Solution:** Check that the element is properly mounted in the DOM before requesting lock.
|
|
317
|
+
|
|
318
|
+
### Lock releases immediately
|
|
319
|
+
|
|
320
|
+
**Problem:** Pointer lock is released right after requesting.
|
|
321
|
+
|
|
322
|
+
**Solution:** Check browser console for security errors. Ensure the element is visible and focusable.
|
|
323
|
+
|
|
324
|
+
### Movement feels wrong
|
|
325
|
+
|
|
326
|
+
**Problem:** Movement direction or sensitivity is incorrect.
|
|
327
|
+
|
|
328
|
+
**Solution:** Adjust the movement multiplier in your `onMove` handler. You may need to negate or scale the values.
|
|
329
|
+
|
|
330
|
+
## See Also
|
|
331
|
+
|
|
332
|
+
- [Pointer Lock API - MDN](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API)
|
|
333
|
+
- [DraggableIcon Component](../components/DraggableIcon.tsx) - Reference implementation
|
|
334
|
+
|