@onerjs/shared-ui-components 8.33.2 → 8.33.4
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/fluent/hoc/childWindow.d.ts +61 -0
- package/fluent/hoc/childWindow.js +174 -0
- package/fluent/hoc/childWindow.js.map +1 -0
- package/fluent/hoc/propertyLines/entitySelectorPropertyLine.d.ts +15 -0
- package/fluent/hoc/propertyLines/entitySelectorPropertyLine.js +9 -0
- package/fluent/hoc/propertyLines/entitySelectorPropertyLine.js.map +1 -0
- package/fluent/primitives/colorPicker.d.ts +8 -1
- package/fluent/primitives/colorPicker.js +11 -10
- package/fluent/primitives/colorPicker.js.map +1 -1
- package/fluent/primitives/comboBox.d.ts +19 -3
- package/fluent/primitives/comboBox.js +16 -11
- package/fluent/primitives/comboBox.js.map +1 -1
- package/fluent/primitives/entitySelector.d.ts +33 -0
- package/fluent/primitives/entitySelector.js +31 -0
- package/fluent/primitives/entitySelector.js.map +1 -0
- package/fluent/primitives/materialSelector.d.ts +21 -0
- package/fluent/primitives/materialSelector.js +16 -0
- package/fluent/primitives/materialSelector.js.map +1 -0
- package/fluent/primitives/nodeSelector.d.ts +21 -0
- package/fluent/primitives/nodeSelector.js +16 -0
- package/fluent/primitives/nodeSelector.js.map +1 -0
- package/fluent/primitives/popover.d.ts +2 -2
- package/fluent/primitives/popover.js +4 -4
- package/fluent/primitives/popover.js.map +1 -1
- package/fluent/primitives/{chooseTexture.d.ts → textureSelector.d.ts} +4 -4
- package/fluent/primitives/textureSelector.js +29 -0
- package/fluent/primitives/textureSelector.js.map +1 -0
- package/package.json +1 -1
- package/fluent/hoc/propertyLines/chooseTexturePropertyLine.d.ts +0 -14
- package/fluent/hoc/propertyLines/chooseTexturePropertyLine.js +0 -14
- package/fluent/hoc/propertyLines/chooseTexturePropertyLine.js.map +0 -1
- package/fluent/primitives/chooseTexture.js +0 -39
- package/fluent/primitives/chooseTexture.js.map +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { FunctionComponent, PropsWithChildren, Ref } from "react";
|
|
2
|
+
export type ChildWindowOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* The default width of the child window in pixels.
|
|
5
|
+
* @remarks Ignored if the ChildWindow was passed an id and previous bounds were saved.
|
|
6
|
+
*/
|
|
7
|
+
defaultWidth?: number;
|
|
8
|
+
/**
|
|
9
|
+
* The default height of the child window in pixels.
|
|
10
|
+
* @remarks Ignored if the ChildWindow was passed an id and previous bounds were saved.
|
|
11
|
+
*/
|
|
12
|
+
defaultHeight?: number;
|
|
13
|
+
/**
|
|
14
|
+
* The default left position of the child window in pixels.
|
|
15
|
+
* @remarks Ignored if the ChildWindow was passed an id and previous bounds were saved.
|
|
16
|
+
*/
|
|
17
|
+
defaultLeft?: number;
|
|
18
|
+
/**
|
|
19
|
+
* The default top position of the child window in pixels.
|
|
20
|
+
* @remarks Ignored if the ChildWindow was passed an id and previous bounds were saved.
|
|
21
|
+
*/
|
|
22
|
+
defaultTop?: number;
|
|
23
|
+
/**
|
|
24
|
+
* The title of the child window.
|
|
25
|
+
* @remarks If not provided, the id will be used instead (if any).
|
|
26
|
+
*/
|
|
27
|
+
title?: string;
|
|
28
|
+
};
|
|
29
|
+
export type ChildWindow = {
|
|
30
|
+
/**
|
|
31
|
+
* Opens the child window.
|
|
32
|
+
* @param options Options for opening the child window.
|
|
33
|
+
*/
|
|
34
|
+
open: (options?: ChildWindowOptions) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Closes the child window.
|
|
37
|
+
*/
|
|
38
|
+
close: () => void;
|
|
39
|
+
};
|
|
40
|
+
export type ChildWindowProps = {
|
|
41
|
+
/**
|
|
42
|
+
* An optional unique identity for the child window.
|
|
43
|
+
* @remarks If provided, the child window's bounds will be saved/restored using this identity.
|
|
44
|
+
*/
|
|
45
|
+
id?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Called when the open state of the child window changes.
|
|
48
|
+
* @param isOpen Whether the child window is open.
|
|
49
|
+
*/
|
|
50
|
+
onOpenChange?: (isOpen: boolean) => void;
|
|
51
|
+
/**
|
|
52
|
+
* A ref that exposes the ChildWindow imperative API.
|
|
53
|
+
*/
|
|
54
|
+
imperativeRef?: Ref<ChildWindow>;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Allows displaying a child window that can contain child components.
|
|
58
|
+
* @param props Props for the child window.
|
|
59
|
+
* @returns The child window component.
|
|
60
|
+
*/
|
|
61
|
+
export declare const ChildWindow: FunctionComponent<PropsWithChildren<ChildWindowProps>>;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createDOMRenderer, FluentProvider, makeStyles, Portal, RendererProvider } from "@fluentui/react-components";
|
|
3
|
+
import { useCallback, useEffect, useImperativeHandle, useState } from "react";
|
|
4
|
+
import { Logger } from "@onerjs/core/Misc/logger.js";
|
|
5
|
+
function ToFeaturesString(options) {
|
|
6
|
+
const { defaultWidth, defaultHeight, defaultLeft, defaultTop } = options;
|
|
7
|
+
const features = [];
|
|
8
|
+
if (defaultWidth !== undefined) {
|
|
9
|
+
features.push({ key: "width", value: defaultWidth.toString() });
|
|
10
|
+
}
|
|
11
|
+
if (defaultHeight !== undefined) {
|
|
12
|
+
features.push({ key: "height", value: defaultHeight.toString() });
|
|
13
|
+
}
|
|
14
|
+
if (defaultLeft !== undefined) {
|
|
15
|
+
features.push({ key: "left", value: defaultLeft.toString() });
|
|
16
|
+
}
|
|
17
|
+
if (defaultTop !== undefined) {
|
|
18
|
+
features.push({ key: "top", value: defaultTop.toString() });
|
|
19
|
+
}
|
|
20
|
+
features.push({ key: "location", value: "no" });
|
|
21
|
+
return features.map((feature) => `${feature.key}=${feature.value}`).join(",");
|
|
22
|
+
}
|
|
23
|
+
const useStyles = makeStyles({
|
|
24
|
+
container: {
|
|
25
|
+
display: "flex",
|
|
26
|
+
flexGrow: 1,
|
|
27
|
+
flexDirection: "column",
|
|
28
|
+
overflow: "hidden",
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* Allows displaying a child window that can contain child components.
|
|
33
|
+
* @param props Props for the child window.
|
|
34
|
+
* @returns The child window component.
|
|
35
|
+
*/
|
|
36
|
+
export const ChildWindow = (props) => {
|
|
37
|
+
const { id, children, onOpenChange, imperativeRef: imperativeRef } = props;
|
|
38
|
+
const classes = useStyles();
|
|
39
|
+
const [windowState, setWindowState] = useState();
|
|
40
|
+
const [childWindow, setChildWindow] = useState();
|
|
41
|
+
const storageKey = id ? `Babylon/Settings/ChildWindow/${id}/Bounds` : null;
|
|
42
|
+
// This function is just for creating the child window itself. It is a function because
|
|
43
|
+
// it must be called synchronously in response to a user interaction (e.g. button click),
|
|
44
|
+
// otherwise the browser will block it as a scripted popup.
|
|
45
|
+
const createWindow = useCallback((options = {}) => {
|
|
46
|
+
if (storageKey) {
|
|
47
|
+
// If we are persisting window bounds, but the window is already open, just use the existing bounds.
|
|
48
|
+
// Otherwise, try to load bounds from storage.
|
|
49
|
+
if (childWindow) {
|
|
50
|
+
options.defaultLeft = childWindow.screenX;
|
|
51
|
+
options.defaultTop = childWindow.screenY;
|
|
52
|
+
options.defaultWidth = childWindow.innerWidth;
|
|
53
|
+
options.defaultHeight = childWindow.innerHeight;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const savedBounds = localStorage.getItem(storageKey);
|
|
57
|
+
if (savedBounds) {
|
|
58
|
+
try {
|
|
59
|
+
const bounds = JSON.parse(savedBounds);
|
|
60
|
+
options.defaultLeft = bounds.left;
|
|
61
|
+
options.defaultTop = bounds.top;
|
|
62
|
+
options.defaultWidth = bounds.width;
|
|
63
|
+
options.defaultHeight = bounds.height;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
Logger.Warn(`Could not parse saved bounds for child window with key ${storageKey}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Half width by default.
|
|
72
|
+
if (!options.defaultWidth) {
|
|
73
|
+
options.defaultWidth = window.innerWidth * (2 / 3);
|
|
74
|
+
}
|
|
75
|
+
// Half height by default.
|
|
76
|
+
if (!options.defaultHeight) {
|
|
77
|
+
options.defaultHeight = window.innerHeight * (2 / 3);
|
|
78
|
+
}
|
|
79
|
+
// Horizontally centered by default.
|
|
80
|
+
if (!options.defaultLeft) {
|
|
81
|
+
options.defaultLeft = window.screenX + (window.innerWidth - options.defaultWidth) * (2 / 3);
|
|
82
|
+
}
|
|
83
|
+
// Vertically centered by default.
|
|
84
|
+
if (!options.defaultTop) {
|
|
85
|
+
options.defaultTop = window.screenY + (window.innerHeight - options.defaultHeight) * (2 / 3);
|
|
86
|
+
}
|
|
87
|
+
// Try to create the child window (can be null if popups are blocked).
|
|
88
|
+
const newChildWindow = window.open("", "", ToFeaturesString(options));
|
|
89
|
+
if (newChildWindow) {
|
|
90
|
+
// Set the title if provided.
|
|
91
|
+
newChildWindow.document.title = options.title ?? id ?? "";
|
|
92
|
+
// Set the child window state.
|
|
93
|
+
setChildWindow((current) => {
|
|
94
|
+
// But first close any existing child window.
|
|
95
|
+
current?.close();
|
|
96
|
+
return newChildWindow;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}, [childWindow, storageKey]);
|
|
100
|
+
useImperativeHandle(imperativeRef, () => {
|
|
101
|
+
return {
|
|
102
|
+
open: createWindow,
|
|
103
|
+
close: () => setChildWindow(undefined),
|
|
104
|
+
};
|
|
105
|
+
}, [createWindow]);
|
|
106
|
+
// This side effect runs any time the child window instance changes. It does the rest of the child window
|
|
107
|
+
// setup work, including creating resources and state needed to properly render the content of the child window.
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
const disposeActions = [];
|
|
110
|
+
if (childWindow) {
|
|
111
|
+
const body = childWindow.document.body;
|
|
112
|
+
body.style.width = "100%";
|
|
113
|
+
body.style.height = "100%";
|
|
114
|
+
body.style.margin = "0";
|
|
115
|
+
body.style.padding = "0";
|
|
116
|
+
body.style.display = "flex";
|
|
117
|
+
body.style.overflow = "hidden";
|
|
118
|
+
const applyWindowState = () => {
|
|
119
|
+
// Setup the window state, including creating a Fluent/Griffel "renderer" for managing runtime styles/classes in the child window.
|
|
120
|
+
setWindowState({ mountNode: body, renderer: createDOMRenderer(childWindow.document) });
|
|
121
|
+
onOpenChange?.(true);
|
|
122
|
+
};
|
|
123
|
+
// Once the child window document is ready, setup the window state which will trigger another effect that renders into the child window.
|
|
124
|
+
if (childWindow.document.readyState === "complete") {
|
|
125
|
+
applyWindowState();
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const onChildWindowLoad = () => {
|
|
129
|
+
applyWindowState();
|
|
130
|
+
};
|
|
131
|
+
childWindow.addEventListener("load", onChildWindowLoad, { once: true });
|
|
132
|
+
disposeActions.push(() => childWindow.removeEventListener("load", onChildWindowLoad));
|
|
133
|
+
}
|
|
134
|
+
// When the child window is closed for any reason, transition back to a closed state.
|
|
135
|
+
const onChildWindowUnload = () => {
|
|
136
|
+
setWindowState(undefined);
|
|
137
|
+
setChildWindow(undefined);
|
|
138
|
+
onOpenChange?.(false);
|
|
139
|
+
};
|
|
140
|
+
childWindow.addEventListener("unload", onChildWindowUnload, { once: true });
|
|
141
|
+
disposeActions.push(() => childWindow.removeEventListener("unload", onChildWindowUnload));
|
|
142
|
+
// If the main window closes, close any open child windows as well (don't leave them orphaned).
|
|
143
|
+
const onParentWindowUnload = () => {
|
|
144
|
+
childWindow.close();
|
|
145
|
+
};
|
|
146
|
+
window.addEventListener("unload", onParentWindowUnload, { once: true });
|
|
147
|
+
disposeActions.push(() => window.removeEventListener("unload", onParentWindowUnload));
|
|
148
|
+
// On dispose, close the child window.
|
|
149
|
+
disposeActions.push(() => childWindow.close());
|
|
150
|
+
// On dispose, save the window bounds.
|
|
151
|
+
disposeActions.push(() => {
|
|
152
|
+
if (storageKey) {
|
|
153
|
+
localStorage.setItem(storageKey, JSON.stringify({
|
|
154
|
+
left: childWindow.screenX,
|
|
155
|
+
top: childWindow.screenY,
|
|
156
|
+
width: childWindow.innerWidth,
|
|
157
|
+
height: childWindow.innerHeight,
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return () => {
|
|
163
|
+
disposeActions.reverse().forEach((dispose) => dispose());
|
|
164
|
+
};
|
|
165
|
+
}, [childWindow]);
|
|
166
|
+
if (!windowState) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
const { mountNode, renderer } = windowState;
|
|
170
|
+
return (
|
|
171
|
+
// Portal targets the body of the child window.
|
|
172
|
+
_jsx(Portal, { mountNode: mountNode, children: _jsx(RendererProvider, { renderer: renderer, targetDocument: mountNode.ownerDocument, children: _jsx(FluentProvider, { className: classes.container, applyStylesToPortals: false, targetDocument: mountNode.ownerDocument, children: children }) }) }));
|
|
173
|
+
};
|
|
174
|
+
//# sourceMappingURL=childWindow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"childWindow.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/hoc/childWindow.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { GriffelRenderer } from \"@fluentui/react-components\";\r\nimport type { FunctionComponent, PropsWithChildren, Ref } from \"react\";\r\n\r\nimport { createDOMRenderer, FluentProvider, makeStyles, Portal, RendererProvider } from \"@fluentui/react-components\";\r\nimport { useCallback, useEffect, useImperativeHandle, useState } from \"react\";\r\n\r\nimport { Logger } from \"core/Misc/logger\";\r\n\r\nfunction ToFeaturesString(options: ChildWindowOptions) {\r\n const { defaultWidth, defaultHeight, defaultLeft, defaultTop } = options;\r\n\r\n const features: { key: string; value: string }[] = [];\r\n\r\n if (defaultWidth !== undefined) {\r\n features.push({ key: \"width\", value: defaultWidth.toString() });\r\n }\r\n if (defaultHeight !== undefined) {\r\n features.push({ key: \"height\", value: defaultHeight.toString() });\r\n }\r\n if (defaultLeft !== undefined) {\r\n features.push({ key: \"left\", value: defaultLeft.toString() });\r\n }\r\n if (defaultTop !== undefined) {\r\n features.push({ key: \"top\", value: defaultTop.toString() });\r\n }\r\n features.push({ key: \"location\", value: \"no\" });\r\n\r\n return features.map((feature) => `${feature.key}=${feature.value}`).join(\",\");\r\n}\r\n\r\nconst useStyles = makeStyles({\r\n container: {\r\n display: \"flex\",\r\n flexGrow: 1,\r\n flexDirection: \"column\",\r\n overflow: \"hidden\",\r\n },\r\n});\r\n\r\nexport type ChildWindowOptions = {\r\n /**\r\n * The default width of the child window in pixels.\r\n * @remarks Ignored if the ChildWindow was passed an id and previous bounds were saved.\r\n */\r\n defaultWidth?: number;\r\n\r\n /**\r\n * The default height of the child window in pixels.\r\n * @remarks Ignored if the ChildWindow was passed an id and previous bounds were saved.\r\n */\r\n defaultHeight?: number;\r\n\r\n /**\r\n * The default left position of the child window in pixels.\r\n * @remarks Ignored if the ChildWindow was passed an id and previous bounds were saved.\r\n */\r\n defaultLeft?: number;\r\n\r\n /**\r\n * The default top position of the child window in pixels.\r\n * @remarks Ignored if the ChildWindow was passed an id and previous bounds were saved.\r\n */\r\n defaultTop?: number;\r\n\r\n /**\r\n * The title of the child window.\r\n * @remarks If not provided, the id will be used instead (if any).\r\n */\r\n title?: string;\r\n};\r\n\r\nexport type ChildWindow = {\r\n /**\r\n * Opens the child window.\r\n * @param options Options for opening the child window.\r\n */\r\n open: (options?: ChildWindowOptions) => void;\r\n\r\n /**\r\n * Closes the child window.\r\n */\r\n close: () => void;\r\n};\r\n\r\nexport type ChildWindowProps = {\r\n /**\r\n * An optional unique identity for the child window.\r\n * @remarks If provided, the child window's bounds will be saved/restored using this identity.\r\n */\r\n id?: string;\r\n\r\n /**\r\n * Called when the open state of the child window changes.\r\n * @param isOpen Whether the child window is open.\r\n */\r\n onOpenChange?: (isOpen: boolean) => void;\r\n\r\n /**\r\n * A ref that exposes the ChildWindow imperative API.\r\n */\r\n imperativeRef?: Ref<ChildWindow>;\r\n};\r\n\r\n/**\r\n * Allows displaying a child window that can contain child components.\r\n * @param props Props for the child window.\r\n * @returns The child window component.\r\n */\r\nexport const ChildWindow: FunctionComponent<PropsWithChildren<ChildWindowProps>> = (props) => {\r\n const { id, children, onOpenChange, imperativeRef: imperativeRef } = props;\r\n const classes = useStyles();\r\n\r\n const [windowState, setWindowState] = useState<{ mountNode: HTMLElement; renderer: GriffelRenderer }>();\r\n const [childWindow, setChildWindow] = useState<Window>();\r\n\r\n const storageKey = id ? `Babylon/Settings/ChildWindow/${id}/Bounds` : null;\r\n\r\n // This function is just for creating the child window itself. It is a function because\r\n // it must be called synchronously in response to a user interaction (e.g. button click),\r\n // otherwise the browser will block it as a scripted popup.\r\n const createWindow = useCallback(\r\n (options: ChildWindowOptions = {}) => {\r\n if (storageKey) {\r\n // If we are persisting window bounds, but the window is already open, just use the existing bounds.\r\n // Otherwise, try to load bounds from storage.\r\n if (childWindow) {\r\n options.defaultLeft = childWindow.screenX;\r\n options.defaultTop = childWindow.screenY;\r\n options.defaultWidth = childWindow.innerWidth;\r\n options.defaultHeight = childWindow.innerHeight;\r\n } else {\r\n const savedBounds = localStorage.getItem(storageKey);\r\n if (savedBounds) {\r\n try {\r\n const bounds = JSON.parse(savedBounds);\r\n options.defaultLeft = bounds.left;\r\n options.defaultTop = bounds.top;\r\n options.defaultWidth = bounds.width;\r\n options.defaultHeight = bounds.height;\r\n } catch {\r\n Logger.Warn(`Could not parse saved bounds for child window with key ${storageKey}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Half width by default.\r\n if (!options.defaultWidth) {\r\n options.defaultWidth = window.innerWidth * (2 / 3);\r\n }\r\n // Half height by default.\r\n if (!options.defaultHeight) {\r\n options.defaultHeight = window.innerHeight * (2 / 3);\r\n }\r\n // Horizontally centered by default.\r\n if (!options.defaultLeft) {\r\n options.defaultLeft = window.screenX + (window.innerWidth - options.defaultWidth) * (2 / 3);\r\n }\r\n // Vertically centered by default.\r\n if (!options.defaultTop) {\r\n options.defaultTop = window.screenY + (window.innerHeight - options.defaultHeight) * (2 / 3);\r\n }\r\n\r\n // Try to create the child window (can be null if popups are blocked).\r\n const newChildWindow = window.open(\"\", \"\", ToFeaturesString(options));\r\n if (newChildWindow) {\r\n // Set the title if provided.\r\n newChildWindow.document.title = options.title ?? id ?? \"\";\r\n\r\n // Set the child window state.\r\n setChildWindow((current) => {\r\n // But first close any existing child window.\r\n current?.close();\r\n return newChildWindow;\r\n });\r\n }\r\n },\r\n [childWindow, storageKey]\r\n );\r\n\r\n useImperativeHandle(imperativeRef, () => {\r\n return {\r\n open: createWindow,\r\n close: () => setChildWindow(undefined),\r\n };\r\n }, [createWindow]);\r\n\r\n // This side effect runs any time the child window instance changes. It does the rest of the child window\r\n // setup work, including creating resources and state needed to properly render the content of the child window.\r\n useEffect(() => {\r\n const disposeActions: (() => void)[] = [];\r\n\r\n if (childWindow) {\r\n const body = childWindow.document.body;\r\n body.style.width = \"100%\";\r\n body.style.height = \"100%\";\r\n body.style.margin = \"0\";\r\n body.style.padding = \"0\";\r\n body.style.display = \"flex\";\r\n body.style.overflow = \"hidden\";\r\n\r\n const applyWindowState = () => {\r\n // Setup the window state, including creating a Fluent/Griffel \"renderer\" for managing runtime styles/classes in the child window.\r\n setWindowState({ mountNode: body, renderer: createDOMRenderer(childWindow.document) });\r\n onOpenChange?.(true);\r\n };\r\n\r\n // Once the child window document is ready, setup the window state which will trigger another effect that renders into the child window.\r\n if (childWindow.document.readyState === \"complete\") {\r\n applyWindowState();\r\n } else {\r\n const onChildWindowLoad = () => {\r\n applyWindowState();\r\n };\r\n childWindow.addEventListener(\"load\", onChildWindowLoad, { once: true });\r\n disposeActions.push(() => childWindow.removeEventListener(\"load\", onChildWindowLoad));\r\n }\r\n\r\n // When the child window is closed for any reason, transition back to a closed state.\r\n const onChildWindowUnload = () => {\r\n setWindowState(undefined);\r\n setChildWindow(undefined);\r\n onOpenChange?.(false);\r\n };\r\n childWindow.addEventListener(\"unload\", onChildWindowUnload, { once: true });\r\n disposeActions.push(() => childWindow.removeEventListener(\"unload\", onChildWindowUnload));\r\n\r\n // If the main window closes, close any open child windows as well (don't leave them orphaned).\r\n const onParentWindowUnload = () => {\r\n childWindow.close();\r\n };\r\n window.addEventListener(\"unload\", onParentWindowUnload, { once: true });\r\n disposeActions.push(() => window.removeEventListener(\"unload\", onParentWindowUnload));\r\n\r\n // On dispose, close the child window.\r\n disposeActions.push(() => childWindow.close());\r\n\r\n // On dispose, save the window bounds.\r\n disposeActions.push(() => {\r\n if (storageKey) {\r\n localStorage.setItem(\r\n storageKey,\r\n JSON.stringify({\r\n left: childWindow.screenX,\r\n top: childWindow.screenY,\r\n width: childWindow.innerWidth,\r\n height: childWindow.innerHeight,\r\n })\r\n );\r\n }\r\n });\r\n }\r\n\r\n return () => {\r\n disposeActions.reverse().forEach((dispose) => dispose());\r\n };\r\n }, [childWindow]);\r\n\r\n if (!windowState) {\r\n return null;\r\n }\r\n\r\n const { mountNode, renderer } = windowState;\r\n\r\n return (\r\n // Portal targets the body of the child window.\r\n <Portal mountNode={mountNode}>\r\n {/* RenderProvider manages Fluent style/class state. */}\r\n <RendererProvider renderer={renderer} targetDocument={mountNode.ownerDocument}>\r\n {/* Fluent Provider is needed for managing other Fluent state and applying the current theme mode. */}\r\n <FluentProvider className={classes.container} applyStylesToPortals={false} targetDocument={mountNode.ownerDocument}>\r\n {children}\r\n </FluentProvider>\r\n </RendererProvider>\r\n </Portal>\r\n );\r\n};\r\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Node } from "@onerjs/core/node.js";
|
|
2
|
+
import type { Nullable } from "@onerjs/core/types.js";
|
|
3
|
+
import type { PropertyLineProps } from "./propertyLine.js";
|
|
4
|
+
import type { NodeSelectorProps } from "../../primitives/nodeSelector.js";
|
|
5
|
+
import type { MaterialSelectorProps } from "../../primitives/materialSelector.js";
|
|
6
|
+
import type { TextureSelectorProps } from "../../primitives/textureSelector.js";
|
|
7
|
+
import type { Material } from "@onerjs/core/Materials/material.js";
|
|
8
|
+
import type { BaseTexture } from "@onerjs/core/Materials/Textures/baseTexture.js";
|
|
9
|
+
type NodeSelectorPropertyLineProps = PropertyLineProps<Nullable<Node>> & NodeSelectorProps;
|
|
10
|
+
type MaterialSelectorPropertyLineProps = PropertyLineProps<Nullable<Material>> & MaterialSelectorProps;
|
|
11
|
+
type TextureSelectorPropertyLineProps = PropertyLineProps<Nullable<BaseTexture>> & TextureSelectorProps;
|
|
12
|
+
export declare const NodeSelectorPropertyLine: (props: NodeSelectorPropertyLineProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare const MaterialSelectorPropertyLine: (props: MaterialSelectorPropertyLineProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare const TextureSelectorPropertyLine: (props: TextureSelectorPropertyLineProps) => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { MaterialSelector } from "../../primitives/materialSelector.js";
|
|
3
|
+
import { PropertyLine } from "./propertyLine.js";
|
|
4
|
+
import { NodeSelector } from "../../primitives/nodeSelector.js";
|
|
5
|
+
import { TextureSelector } from "../../primitives/textureSelector.js";
|
|
6
|
+
export const NodeSelectorPropertyLine = (props) => _jsx(PropertyLine, { ...props, children: _jsx(NodeSelector, { ...props }) });
|
|
7
|
+
export const MaterialSelectorPropertyLine = (props) => _jsx(PropertyLine, { ...props, children: _jsx(MaterialSelector, { ...props }) });
|
|
8
|
+
export const TextureSelectorPropertyLine = (props) => _jsx(PropertyLine, { ...props, children: _jsx(TextureSelector, { ...props }) });
|
|
9
|
+
//# sourceMappingURL=entitySelectorPropertyLine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitySelectorPropertyLine.js","sourceRoot":"","sources":["../../../../../../dev/sharedUiComponents/src/fluent/hoc/propertyLines/entitySelectorPropertyLine.tsx"],"names":[],"mappings":";AASA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAKnE,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,KAAoC,EAAE,EAAE,CAAC,KAAC,YAAY,OAAK,KAAK,EAAE,QAAQ,EAAE,KAAC,YAAY,OAAK,KAAK,GAAI,GAAI,CAAC;AACrJ,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,KAAwC,EAAE,EAAE,CAAC,KAAC,YAAY,OAAK,KAAK,EAAE,QAAQ,EAAE,KAAC,gBAAgB,OAAK,KAAK,GAAI,GAAI,CAAC;AACjK,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,KAAuC,EAAE,EAAE,CAAC,KAAC,YAAY,OAAK,KAAK,EAAE,QAAQ,EAAE,KAAC,eAAe,OAAK,KAAK,GAAI,GAAI,CAAC","sourcesContent":["import type { Node } from \"core/node\";\r\nimport type { Nullable } from \"core/types\";\r\nimport type { PropertyLineProps } from \"./propertyLine\";\r\nimport type { NodeSelectorProps } from \"../../primitives/nodeSelector\";\r\nimport type { MaterialSelectorProps } from \"../../primitives/materialSelector\";\r\nimport type { TextureSelectorProps } from \"../../primitives/textureSelector\";\r\nimport type { Material } from \"core/Materials/material\";\r\nimport type { BaseTexture } from \"core/Materials/Textures/baseTexture\";\r\n\r\nimport { MaterialSelector } from \"../../primitives/materialSelector\";\r\nimport { PropertyLine } from \"./propertyLine\";\r\nimport { NodeSelector } from \"../../primitives/nodeSelector\";\r\nimport { TextureSelector } from \"../../primitives/textureSelector\";\r\ntype NodeSelectorPropertyLineProps = PropertyLineProps<Nullable<Node>> & NodeSelectorProps;\r\ntype MaterialSelectorPropertyLineProps = PropertyLineProps<Nullable<Material>> & MaterialSelectorProps;\r\ntype TextureSelectorPropertyLineProps = PropertyLineProps<Nullable<BaseTexture>> & TextureSelectorProps;\r\n\r\nexport const NodeSelectorPropertyLine = (props: NodeSelectorPropertyLineProps) => <PropertyLine {...props} children={<NodeSelector {...props} />} />;\r\nexport const MaterialSelectorPropertyLine = (props: MaterialSelectorPropertyLineProps) => <PropertyLine {...props} children={<MaterialSelector {...props} />} />;\r\nexport const TextureSelectorPropertyLine = (props: TextureSelectorPropertyLineProps) => <PropertyLine {...props} children={<TextureSelector {...props} />} />;\r\n"]}
|
|
@@ -4,7 +4,14 @@ import type { PrimitiveProps } from "./primitive.js";
|
|
|
4
4
|
export type ColorPickerProps<C extends Color3 | Color4> = {
|
|
5
5
|
isLinearMode?: boolean;
|
|
6
6
|
} & PrimitiveProps<C>;
|
|
7
|
-
export declare const ColorPickerPopup:
|
|
7
|
+
export declare const ColorPickerPopup: import("react").ForwardRefExoticComponent<{
|
|
8
|
+
isLinearMode?: boolean;
|
|
9
|
+
} & import("./primitive.js").BasePrimitiveProps & {
|
|
10
|
+
value: Color3 | Color4;
|
|
11
|
+
infoLabel?: import("./infoLabel.js").InfoLabelParentProps;
|
|
12
|
+
} & {
|
|
13
|
+
onChange: (value: Color3 | Color4) => void;
|
|
14
|
+
} & import("react").RefAttributes<HTMLButtonElement>>;
|
|
8
15
|
export type InputHexProps = PrimitiveProps<Color3 | Color4> & {
|
|
9
16
|
linearHex?: boolean;
|
|
10
17
|
isLinearMode?: boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/* eslint-disable jsdoc/require-returns */
|
|
3
3
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
4
|
-
import { useState, useEffect, useCallback, useContext } from "react";
|
|
4
|
+
import { forwardRef, useState, useEffect, useCallback, useContext } from "react";
|
|
5
5
|
import { ColorPicker as FluentColorPicker, ColorSlider, ColorArea, AlphaSlider, makeStyles, tokens, Body1Strong, ColorSwatch, Body1 } from "@fluentui/react-components";
|
|
6
6
|
import { Color3, Color4 } from "@onerjs/core/Maths/math.color.js";
|
|
7
7
|
import { SpinButton } from "./spinButton.js";
|
|
@@ -58,28 +58,29 @@ const useColorPickerStyles = makeStyles({
|
|
|
58
58
|
gap: tokens.spacingVerticalSNudge, // 6px
|
|
59
59
|
},
|
|
60
60
|
});
|
|
61
|
-
export const ColorPickerPopup = (props) => {
|
|
61
|
+
export const ColorPickerPopup = forwardRef((props, ref) => {
|
|
62
62
|
ColorPickerPopup.displayName = "ColorPickerPopup";
|
|
63
|
+
const { value, onChange, isLinearMode, ...rest } = props;
|
|
63
64
|
const classes = useColorPickerStyles();
|
|
64
|
-
const [color, setColor] = useState(
|
|
65
|
-
const [isLinear, setIsLinear] = useState(
|
|
65
|
+
const [color, setColor] = useState(value);
|
|
66
|
+
const [isLinear, setIsLinear] = useState(isLinearMode ?? false);
|
|
66
67
|
const [isFloat, setFloat] = useState(false);
|
|
67
68
|
const { size } = useContext(ToolContext);
|
|
68
69
|
useEffect(() => {
|
|
69
|
-
setColor(
|
|
70
|
-
}, [
|
|
70
|
+
setColor(value); // Ensures the trigger color updates when props.value changes
|
|
71
|
+
}, [value]);
|
|
71
72
|
const handleColorPickerChange = (_, data) => {
|
|
72
73
|
let color = Color3.FromHSV(data.color.h, data.color.s, data.color.v);
|
|
73
|
-
if (
|
|
74
|
+
if (value instanceof Color4) {
|
|
74
75
|
color = Color4.FromColor3(color, data.color.a ?? 1);
|
|
75
76
|
}
|
|
76
77
|
handleChange(color);
|
|
77
78
|
};
|
|
78
79
|
const handleChange = (newColor) => {
|
|
79
80
|
setColor(newColor);
|
|
80
|
-
|
|
81
|
+
onChange(newColor); // Ensures the parent is notified when color changes from within colorPicker
|
|
81
82
|
};
|
|
82
|
-
return (_jsx(Popover, { trigger: _jsx(ColorSwatch, { borderColor: tokens.colorNeutralShadowKeyDarker, size: size === "small" ? "extra-small" : "small", shape: "rounded", color: color.toHexString(), value: color.toHexString().slice(1) }), children: _jsxs("div", { className: classes.container, children: [_jsxs(FluentColorPicker, { className: classes.colorPicker, color: rgbaToHsv(color), onColorChange: handleColorPickerChange, children: [_jsx(ColorArea, { inputX: { "aria-label": "Saturation" }, inputY: { "aria-label": "Brightness" } }), _jsx(ColorSlider, { "aria-label": "Hue" }), color instanceof Color4 && _jsx(AlphaSlider, { "aria-label": "Alpha" })] }), _jsxs("div", { className: classes.row, children: [_jsx("div", { className: classes.previewColor, style: { backgroundColor: color.toHexString() } }), _jsx(NumberDropdown, { className: classes.inputField, infoLabel: {
|
|
83
|
+
return (_jsx(Popover, { trigger: _jsx(ColorSwatch, { ref: ref, ...rest, borderColor: tokens.colorNeutralShadowKeyDarker, size: size === "small" ? "extra-small" : "small", shape: "rounded", color: color.toHexString(), value: color.toHexString().slice(1) }), children: _jsxs("div", { className: classes.container, children: [_jsxs(FluentColorPicker, { className: classes.colorPicker, color: rgbaToHsv(color), onColorChange: handleColorPickerChange, children: [_jsx(ColorArea, { inputX: { "aria-label": "Saturation" }, inputY: { "aria-label": "Brightness" } }), _jsx(ColorSlider, { "aria-label": "Hue" }), color instanceof Color4 && _jsx(AlphaSlider, { "aria-label": "Alpha" })] }), _jsxs("div", { className: classes.row, children: [_jsx("div", { className: classes.previewColor, style: { backgroundColor: color.toHexString() } }), _jsx(NumberDropdown, { className: classes.inputField, infoLabel: {
|
|
83
84
|
label: "Color Space",
|
|
84
85
|
info: _jsx(Body1, { children: "Today this is not mutable as the color space is determined by the entity. Soon we will allow swapping" }),
|
|
85
86
|
}, options: [
|
|
@@ -92,7 +93,7 @@ export const ColorPickerPopup = (props) => {
|
|
|
92
93
|
{ label: "Int", value: 0 },
|
|
93
94
|
{ label: "Float", value: 1 },
|
|
94
95
|
], disabled: true, value: isFloat ? 1 : 0, onChange: (val) => setFloat(val === 1) })] }), _jsxs("div", { className: classes.inputRow, children: [_jsx(InputRgbField, { title: "Red", value: color, rgbKey: "r", onChange: handleChange }), _jsx(InputRgbField, { title: "Green", value: color, rgbKey: "g", onChange: handleChange }), _jsx(InputRgbField, { title: "Blue", value: color, rgbKey: "b", onChange: handleChange }), _jsx(InputAlphaField, { color: color, onChange: handleChange })] }), _jsxs("div", { className: classes.inputRow, children: [_jsx(InputHsvField, { title: "Hue", value: color, hsvKey: "h", max: 360, onChange: handleChange }), _jsx(InputHsvField, { title: "Saturation", value: color, hsvKey: "s", max: 100, scale: 100, onChange: handleChange }), _jsx(InputHsvField, { title: "Value", value: color, hsvKey: "v", max: 100, scale: 100, onChange: handleChange })] }), _jsx("div", { className: classes.inputRow, children: _jsx(InputHexField, { title: "Hexadecimal", linearHex: isLinear, isLinearMode: isLinear, value: color, onChange: handleChange }) })] }) }));
|
|
95
|
-
};
|
|
96
|
+
});
|
|
96
97
|
/**
|
|
97
98
|
* Component which displays the passed in color's HEX value, either in linearSpace (if linearHex is true) or in gamma space
|
|
98
99
|
* When the hex color is changed by user, component calculates the new Color3/4 value and calls onChange
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"colorPicker.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/colorPicker.tsx"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable jsdoc/require-returns */\r\n/* eslint-disable @typescript-eslint/naming-convention */\r\nimport { useState, useEffect, useCallback, useContext } from \"react\";\r\nimport type { FunctionComponent } from \"react\";\r\nimport { ColorPicker as FluentColorPicker, ColorSlider, ColorArea, AlphaSlider, makeStyles, tokens, Body1Strong, ColorSwatch, Body1 } from \"@fluentui/react-components\";\r\nimport type { ColorPickerProps as FluentColorPickerProps } from \"@fluentui/react-components\";\r\nimport { Color3, Color4 } from \"core/Maths/math.color\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\nimport { SpinButton } from \"./spinButton\";\r\nimport { TextInput } from \"./textInput\";\r\nimport { NumberDropdown } from \"./dropdown\";\r\nimport { ValidateColorHex } from \"./utils\";\r\nimport { Link } from \"./link\";\r\nimport { ToolContext } from \"../hoc/fluentToolWrapper\";\r\nimport { Popover } from \"./popover\";\r\n\r\nconst useColorPickerStyles = makeStyles({\r\n container: {\r\n width: \"350px\",\r\n display: \"flex\", // becomes a flexbox\r\n flexDirection: \"column\", // with children in a column\r\n alignItems: \"center\", // centers children horizontally\r\n justifyContent: \"center\", // centers children vertically (if height is set)\r\n gap: tokens.spacingVerticalM,\r\n overflow: \"visible\",\r\n },\r\n row: {\r\n flex: 1, // is a row in the container's flex column\r\n display: \"flex\", // becomes its own flexbox\r\n flexDirection: \"row\", // with children in a row\r\n gap: tokens.spacingHorizontalXL,\r\n alignItems: \"center\", // align items vertically\r\n width: \"100%\",\r\n },\r\n colorPicker: {\r\n flex: 1,\r\n width: \"350px\",\r\n height: \"350px\",\r\n },\r\n previewColor: {\r\n width: \"60px\",\r\n height: \"60px\",\r\n borderRadius: tokens.borderRadiusMedium, // 4px?\r\n border: `${tokens.spacingVerticalXXS} solid ${tokens.colorNeutralShadowKeyLighter}`,\r\n \"@media (forced-colors: active)\": {\r\n forcedColorAdjust: \"none\", // ensures elmement maintains color in high constrast mode\r\n },\r\n },\r\n inputRow: {\r\n display: \"flex\",\r\n flexDirection: \"row\",\r\n flex: 1, // grow and fill available space\r\n justifyContent: \"center\",\r\n gap: \"10px\",\r\n width: \"100%\",\r\n },\r\n inputField: {\r\n flex: 1, // grow and fill available space\r\n width: \"auto\",\r\n minWidth: 0,\r\n gap: tokens.spacingVerticalSNudge, // 6px\r\n },\r\n});\r\n\r\nexport type ColorPickerProps<C extends Color3 | Color4> = {\r\n isLinearMode?: boolean;\r\n} & PrimitiveProps<C>;\r\n\r\nexport const ColorPickerPopup: FunctionComponent<ColorPickerProps<Color3 | Color4>> = (props) => {\r\n ColorPickerPopup.displayName = \"ColorPickerPopup\";\r\n const classes = useColorPickerStyles();\r\n const [color, setColor] = useState(props.value);\r\n const [isLinear, setIsLinear] = useState(props.isLinearMode ?? false);\r\n const [isFloat, setFloat] = useState(false);\r\n const { size } = useContext(ToolContext);\r\n useEffect(() => {\r\n setColor(props.value); // Ensures the trigger color updates when props.value changes\r\n }, [props.value]);\r\n\r\n const handleColorPickerChange: FluentColorPickerProps[\"onColorChange\"] = (_, data) => {\r\n let color: Color3 | Color4 = Color3.FromHSV(data.color.h, data.color.s, data.color.v);\r\n if (props.value instanceof Color4) {\r\n color = Color4.FromColor3(color, data.color.a ?? 1);\r\n }\r\n handleChange(color);\r\n };\r\n\r\n const handleChange = (newColor: Color3 | Color4) => {\r\n setColor(newColor);\r\n props.onChange(newColor); // Ensures the parent is notified when color changes from within colorPicker\r\n };\r\n\r\n return (\r\n <Popover\r\n trigger={\r\n <ColorSwatch\r\n borderColor={tokens.colorNeutralShadowKeyDarker}\r\n size={size === \"small\" ? \"extra-small\" : \"small\"}\r\n shape=\"rounded\"\r\n color={color.toHexString()}\r\n value={color.toHexString().slice(1)}\r\n />\r\n }\r\n >\r\n <div className={classes.container}>\r\n <FluentColorPicker className={classes.colorPicker} color={rgbaToHsv(color)} onColorChange={handleColorPickerChange}>\r\n <ColorArea inputX={{ \"aria-label\": \"Saturation\" }} inputY={{ \"aria-label\": \"Brightness\" }} />\r\n <ColorSlider aria-label=\"Hue\" />\r\n {color instanceof Color4 && <AlphaSlider aria-label=\"Alpha\" />}\r\n </FluentColorPicker>\r\n {/* Top Row: Preview, Gamma Hex, Linear Hex */}\r\n <div className={classes.row}>\r\n <div className={classes.previewColor} style={{ backgroundColor: color.toHexString() }} />\r\n <NumberDropdown\r\n className={classes.inputField}\r\n infoLabel={{\r\n label: \"Color Space\",\r\n info: <Body1>Today this is not mutable as the color space is determined by the entity. Soon we will allow swapping</Body1>,\r\n }}\r\n options={[\r\n { label: \"Gamma\", value: 0 },\r\n { label: \"Linear\", value: 1 },\r\n ]}\r\n disabled={true}\r\n value={isLinear ? 1 : 0}\r\n onChange={(val: number) => setIsLinear(val === 1)}\r\n />\r\n <NumberDropdown\r\n className={classes.inputField}\r\n infoLabel={{\r\n label: \"Data Type\",\r\n info: <Body1>We will introduce this functionality soon!</Body1>,\r\n }}\r\n options={[\r\n { label: \"Int\", value: 0 },\r\n { label: \"Float\", value: 1 },\r\n ]}\r\n disabled={true}\r\n value={isFloat ? 1 : 0}\r\n onChange={(val: number) => setFloat(val === 1)}\r\n />\r\n </div>\r\n\r\n {/* Middle Row: Red, Green, Blue, Alpha */}\r\n <div className={classes.inputRow}>\r\n <InputRgbField title=\"Red\" value={color} rgbKey=\"r\" onChange={handleChange} />\r\n <InputRgbField title=\"Green\" value={color} rgbKey=\"g\" onChange={handleChange} />\r\n <InputRgbField title=\"Blue\" value={color} rgbKey=\"b\" onChange={handleChange} />\r\n <InputAlphaField color={color} onChange={handleChange} />\r\n </div>\r\n\r\n {/* Bottom Row: Hue, Saturation, Value */}\r\n <div className={classes.inputRow}>\r\n <InputHsvField title=\"Hue\" value={color} hsvKey=\"h\" max={360} onChange={handleChange} />\r\n <InputHsvField title=\"Saturation\" value={color} hsvKey=\"s\" max={100} scale={100} onChange={handleChange} />\r\n <InputHsvField title=\"Value\" value={color} hsvKey=\"v\" max={100} scale={100} onChange={handleChange} />\r\n </div>\r\n\r\n <div className={classes.inputRow}>\r\n <InputHexField title=\"Hexadecimal\" linearHex={isLinear} isLinearMode={isLinear} value={color} onChange={handleChange} />\r\n </div>\r\n </div>\r\n </Popover>\r\n );\r\n};\r\n\r\nexport type InputHexProps = PrimitiveProps<Color3 | Color4> & {\r\n linearHex?: boolean;\r\n isLinearMode?: boolean;\r\n};\r\n/**\r\n * Component which displays the passed in color's HEX value, either in linearSpace (if linearHex is true) or in gamma space\r\n * When the hex color is changed by user, component calculates the new Color3/4 value and calls onChange\r\n *\r\n * Component uses the isLinearMode boolean to display an informative label regarding linear / gamma space\r\n * @param props - The properties for the InputHexField component.\r\n * @returns\r\n */\r\nexport const InputHexField: FunctionComponent<InputHexProps> = (props) => {\r\n const classes = useColorPickerStyles();\r\n const { title, value, onChange, linearHex, isLinearMode } = props;\r\n\r\n return (\r\n <TextInput\r\n disabled={linearHex ? !isLinearMode : false}\r\n className={classes.inputField}\r\n value={linearHex ? value.toLinearSpace().toHexString() : value.toHexString()}\r\n validator={ValidateColorHex}\r\n onChange={(val) => (linearHex ? onChange(Color3.FromHexString(val).toGammaSpace()) : onChange(Color3.FromHexString(val)))}\r\n infoLabel={\r\n title\r\n ? {\r\n label: title,\r\n // If not representing a linearHex, no info is needed.\r\n info: !props.linearHex ? undefined : !isLinearMode ? ( // If representing a linear hex but we are in gammaMode, simple message explaining why linearHex is disabled\r\n <> This color picker is attached to an entity whose color is stored in gamma space, so we are showing linear hex in disabled view </>\r\n ) : (\r\n // If representing a linear hex and we are in linearMode, give information about how to use these hex values\r\n <>\r\n This color picker is attached to an entity whose color is stored in linear space (ex: PBR Material), and Babylon converts the color to gamma space\r\n before rendering on screen because the human eye is best at processing colors in gamma space. We thus also want to display the color picker in\r\n gamma space so that the color chosen here will match the color seen in your entity.\r\n <br />\r\n If you want to copy/paste the HEX into your code, you can either use\r\n <Body1Strong>Color3.FromHexString(LINEAR_HEX)</Body1Strong>\r\n <br />\r\n or\r\n <br />\r\n <Body1Strong>Color3.FromHexString(GAMMA_HEX).toLinearSpace()</Body1Strong>\r\n <br />\r\n <br />\r\n <Link url=\"https://doc.babylonjs.com/preparingArtForBabylon/controllingColorSpace/\" value=\"Read more in our docs!\" />\r\n </>\r\n ),\r\n }\r\n : undefined\r\n }\r\n />\r\n );\r\n};\r\n\r\ntype RgbKey = \"r\" | \"g\" | \"b\";\r\ntype InputRgbFieldProps = PrimitiveProps<Color3 | Color4> & {\r\n rgbKey: RgbKey;\r\n};\r\n\r\nconst InputRgbField: FunctionComponent<InputRgbFieldProps> = (props) => {\r\n const { value, onChange, title, rgbKey } = props;\r\n const classes = useColorPickerStyles();\r\n\r\n const handleChange = useCallback(\r\n (val: number) => {\r\n const newColor = value.clone();\r\n newColor[rgbKey] = val / 255.0; // Convert to 0-1 range\r\n onChange(newColor);\r\n },\r\n [value, onChange, rgbKey]\r\n );\r\n\r\n return (\r\n <SpinButton\r\n title={title}\r\n infoLabel={title ? { label: title } : undefined}\r\n className={classes.inputField}\r\n min={0}\r\n max={255}\r\n value={Math.round(value[rgbKey] * 255)}\r\n forceInt\r\n onChange={handleChange}\r\n />\r\n );\r\n};\r\n\r\nfunction rgbaToHsv(color: { r: number; g: number; b: number; a?: number }): { h: number; s: number; v: number; a?: number } {\r\n const c = new Color3(color.r, color.g, color.b);\r\n const hsv = c.toHSV();\r\n return { h: hsv.r, s: hsv.g, v: hsv.b, a: color.a };\r\n}\r\n\r\ntype HsvKey = \"h\" | \"s\" | \"v\";\r\ntype InputHsvFieldProps = PrimitiveProps<Color3 | Color4> & {\r\n hsvKey: HsvKey;\r\n max: number;\r\n scale?: number;\r\n};\r\n\r\n/**\r\n * In the HSV (Hue, Saturation, Value) color model, Hue (H) ranges from 0 to 360 degrees, representing the color's position on the color wheel.\r\n * Saturation (S) ranges from 0 to 100%, indicating the intensity or purity of the color, with 0 being shades of gray and 100 being a fully saturated color.\r\n * Value (V) ranges from 0 to 100%, representing the brightness of the color, with 0 being black and 100 being the brightest.\r\n * @param props - The properties for the InputHsvField component.\r\n */\r\nexport const InputHsvField: FunctionComponent<InputHsvFieldProps> = (props) => {\r\n const { value, title, hsvKey, max, onChange, scale = 1 } = props;\r\n\r\n const classes = useColorPickerStyles();\r\n\r\n const handleChange = useCallback(\r\n (val: number) => {\r\n // Convert current color to HSV, update the new hsv value, then call onChange prop\r\n const hsv = rgbaToHsv(value);\r\n hsv[hsvKey] = val / scale;\r\n let newColor: Color3 | Color4 = Color3.FromHSV(hsv.h, hsv.s, hsv.v);\r\n if (value instanceof Color4) {\r\n newColor = Color4.FromColor3(newColor, value.a ?? 1);\r\n }\r\n props.onChange(newColor);\r\n },\r\n [value, onChange, hsvKey, scale]\r\n );\r\n\r\n return (\r\n <SpinButton\r\n infoLabel={title ? { label: title } : undefined}\r\n title={title}\r\n className={classes.inputField}\r\n min={0}\r\n max={max}\r\n value={Math.round(rgbaToHsv(value)[hsvKey] * scale)}\r\n forceInt\r\n onChange={handleChange}\r\n />\r\n );\r\n};\r\n\r\ntype InputAlphaProps = {\r\n color: Color3 | Color4;\r\n onChange: (color: Color4) => void;\r\n};\r\n\r\n/**\r\n * Displays the alpha value of a color, either in the disabled state (if color is Color3) or as a spin button (if color is Color4).\r\n * @param props\r\n * @returns\r\n */\r\nconst InputAlphaField: FunctionComponent<InputAlphaProps> = (props) => {\r\n const classes = useColorPickerStyles();\r\n const { color, onChange } = props;\r\n\r\n const handleChange = useCallback(\r\n (value: number) => {\r\n if (Number.isNaN(value) || value < 0 || value > 1) {\r\n return;\r\n }\r\n\r\n if (color instanceof Color4) {\r\n const newColor = color.clone();\r\n newColor.a = value;\r\n return newColor;\r\n } else {\r\n return Color4.FromColor3(color, value);\r\n }\r\n },\r\n [onChange]\r\n );\r\n\r\n return (\r\n <SpinButton\r\n disabled={color instanceof Color3}\r\n min={0}\r\n max={1}\r\n className={classes.inputField}\r\n value={color instanceof Color3 ? 1 : color.a}\r\n step={0.01}\r\n onChange={handleChange}\r\n infoLabel={{\r\n label: \"Alpha\",\r\n info:\r\n color instanceof Color3 ? (\r\n <>\r\n Because this color picker is representing a Color3, we do not permit modifying alpha from the color picker. You can however modify the entity's alpha\r\n property directly, either in code via entity.alpha OR via inspector's transparency section.\r\n </>\r\n ) : undefined,\r\n }}\r\n />\r\n );\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"file":"colorPicker.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/colorPicker.tsx"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable jsdoc/require-returns */\r\n/* eslint-disable @typescript-eslint/naming-convention */\r\nimport { forwardRef, useState, useEffect, useCallback, useContext } from \"react\";\r\nimport type { FunctionComponent } from \"react\";\r\nimport { ColorPicker as FluentColorPicker, ColorSlider, ColorArea, AlphaSlider, makeStyles, tokens, Body1Strong, ColorSwatch, Body1 } from \"@fluentui/react-components\";\r\nimport type { ColorPickerProps as FluentColorPickerProps } from \"@fluentui/react-components\";\r\nimport { Color3, Color4 } from \"core/Maths/math.color\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\nimport { SpinButton } from \"./spinButton\";\r\nimport { TextInput } from \"./textInput\";\r\nimport { NumberDropdown } from \"./dropdown\";\r\nimport { ValidateColorHex } from \"./utils\";\r\nimport { Link } from \"./link\";\r\nimport { ToolContext } from \"../hoc/fluentToolWrapper\";\r\nimport { Popover } from \"./popover\";\r\n\r\nconst useColorPickerStyles = makeStyles({\r\n container: {\r\n width: \"350px\",\r\n display: \"flex\", // becomes a flexbox\r\n flexDirection: \"column\", // with children in a column\r\n alignItems: \"center\", // centers children horizontally\r\n justifyContent: \"center\", // centers children vertically (if height is set)\r\n gap: tokens.spacingVerticalM,\r\n overflow: \"visible\",\r\n },\r\n row: {\r\n flex: 1, // is a row in the container's flex column\r\n display: \"flex\", // becomes its own flexbox\r\n flexDirection: \"row\", // with children in a row\r\n gap: tokens.spacingHorizontalXL,\r\n alignItems: \"center\", // align items vertically\r\n width: \"100%\",\r\n },\r\n colorPicker: {\r\n flex: 1,\r\n width: \"350px\",\r\n height: \"350px\",\r\n },\r\n previewColor: {\r\n width: \"60px\",\r\n height: \"60px\",\r\n borderRadius: tokens.borderRadiusMedium, // 4px?\r\n border: `${tokens.spacingVerticalXXS} solid ${tokens.colorNeutralShadowKeyLighter}`,\r\n \"@media (forced-colors: active)\": {\r\n forcedColorAdjust: \"none\", // ensures elmement maintains color in high constrast mode\r\n },\r\n },\r\n inputRow: {\r\n display: \"flex\",\r\n flexDirection: \"row\",\r\n flex: 1, // grow and fill available space\r\n justifyContent: \"center\",\r\n gap: \"10px\",\r\n width: \"100%\",\r\n },\r\n inputField: {\r\n flex: 1, // grow and fill available space\r\n width: \"auto\",\r\n minWidth: 0,\r\n gap: tokens.spacingVerticalSNudge, // 6px\r\n },\r\n});\r\n\r\nexport type ColorPickerProps<C extends Color3 | Color4> = {\r\n isLinearMode?: boolean;\r\n} & PrimitiveProps<C>;\r\n\r\nexport const ColorPickerPopup = forwardRef<HTMLButtonElement, ColorPickerProps<Color3 | Color4>>((props, ref) => {\r\n ColorPickerPopup.displayName = \"ColorPickerPopup\";\r\n const { value, onChange, isLinearMode, ...rest } = props;\r\n const classes = useColorPickerStyles();\r\n const [color, setColor] = useState(value);\r\n const [isLinear, setIsLinear] = useState(isLinearMode ?? false);\r\n const [isFloat, setFloat] = useState(false);\r\n const { size } = useContext(ToolContext);\r\n useEffect(() => {\r\n setColor(value); // Ensures the trigger color updates when props.value changes\r\n }, [value]);\r\n\r\n const handleColorPickerChange: FluentColorPickerProps[\"onColorChange\"] = (_, data) => {\r\n let color: Color3 | Color4 = Color3.FromHSV(data.color.h, data.color.s, data.color.v);\r\n if (value instanceof Color4) {\r\n color = Color4.FromColor3(color, data.color.a ?? 1);\r\n }\r\n handleChange(color);\r\n };\r\n\r\n const handleChange = (newColor: Color3 | Color4) => {\r\n setColor(newColor);\r\n onChange(newColor); // Ensures the parent is notified when color changes from within colorPicker\r\n };\r\n\r\n return (\r\n <Popover\r\n trigger={\r\n <ColorSwatch\r\n ref={ref}\r\n {...rest}\r\n borderColor={tokens.colorNeutralShadowKeyDarker}\r\n size={size === \"small\" ? \"extra-small\" : \"small\"}\r\n shape=\"rounded\"\r\n color={color.toHexString()}\r\n value={color.toHexString().slice(1)}\r\n />\r\n }\r\n >\r\n <div className={classes.container}>\r\n <FluentColorPicker className={classes.colorPicker} color={rgbaToHsv(color)} onColorChange={handleColorPickerChange}>\r\n <ColorArea inputX={{ \"aria-label\": \"Saturation\" }} inputY={{ \"aria-label\": \"Brightness\" }} />\r\n <ColorSlider aria-label=\"Hue\" />\r\n {color instanceof Color4 && <AlphaSlider aria-label=\"Alpha\" />}\r\n </FluentColorPicker>\r\n {/* Top Row: Preview, Gamma Hex, Linear Hex */}\r\n <div className={classes.row}>\r\n <div className={classes.previewColor} style={{ backgroundColor: color.toHexString() }} />\r\n <NumberDropdown\r\n className={classes.inputField}\r\n infoLabel={{\r\n label: \"Color Space\",\r\n info: <Body1>Today this is not mutable as the color space is determined by the entity. Soon we will allow swapping</Body1>,\r\n }}\r\n options={[\r\n { label: \"Gamma\", value: 0 },\r\n { label: \"Linear\", value: 1 },\r\n ]}\r\n disabled={true}\r\n value={isLinear ? 1 : 0}\r\n onChange={(val: number) => setIsLinear(val === 1)}\r\n />\r\n <NumberDropdown\r\n className={classes.inputField}\r\n infoLabel={{\r\n label: \"Data Type\",\r\n info: <Body1>We will introduce this functionality soon!</Body1>,\r\n }}\r\n options={[\r\n { label: \"Int\", value: 0 },\r\n { label: \"Float\", value: 1 },\r\n ]}\r\n disabled={true}\r\n value={isFloat ? 1 : 0}\r\n onChange={(val: number) => setFloat(val === 1)}\r\n />\r\n </div>\r\n\r\n {/* Middle Row: Red, Green, Blue, Alpha */}\r\n <div className={classes.inputRow}>\r\n <InputRgbField title=\"Red\" value={color} rgbKey=\"r\" onChange={handleChange} />\r\n <InputRgbField title=\"Green\" value={color} rgbKey=\"g\" onChange={handleChange} />\r\n <InputRgbField title=\"Blue\" value={color} rgbKey=\"b\" onChange={handleChange} />\r\n <InputAlphaField color={color} onChange={handleChange} />\r\n </div>\r\n\r\n {/* Bottom Row: Hue, Saturation, Value */}\r\n <div className={classes.inputRow}>\r\n <InputHsvField title=\"Hue\" value={color} hsvKey=\"h\" max={360} onChange={handleChange} />\r\n <InputHsvField title=\"Saturation\" value={color} hsvKey=\"s\" max={100} scale={100} onChange={handleChange} />\r\n <InputHsvField title=\"Value\" value={color} hsvKey=\"v\" max={100} scale={100} onChange={handleChange} />\r\n </div>\r\n\r\n <div className={classes.inputRow}>\r\n <InputHexField title=\"Hexadecimal\" linearHex={isLinear} isLinearMode={isLinear} value={color} onChange={handleChange} />\r\n </div>\r\n </div>\r\n </Popover>\r\n );\r\n});\r\n\r\nexport type InputHexProps = PrimitiveProps<Color3 | Color4> & {\r\n linearHex?: boolean;\r\n isLinearMode?: boolean;\r\n};\r\n/**\r\n * Component which displays the passed in color's HEX value, either in linearSpace (if linearHex is true) or in gamma space\r\n * When the hex color is changed by user, component calculates the new Color3/4 value and calls onChange\r\n *\r\n * Component uses the isLinearMode boolean to display an informative label regarding linear / gamma space\r\n * @param props - The properties for the InputHexField component.\r\n * @returns\r\n */\r\nexport const InputHexField: FunctionComponent<InputHexProps> = (props) => {\r\n const classes = useColorPickerStyles();\r\n const { title, value, onChange, linearHex, isLinearMode } = props;\r\n\r\n return (\r\n <TextInput\r\n disabled={linearHex ? !isLinearMode : false}\r\n className={classes.inputField}\r\n value={linearHex ? value.toLinearSpace().toHexString() : value.toHexString()}\r\n validator={ValidateColorHex}\r\n onChange={(val) => (linearHex ? onChange(Color3.FromHexString(val).toGammaSpace()) : onChange(Color3.FromHexString(val)))}\r\n infoLabel={\r\n title\r\n ? {\r\n label: title,\r\n // If not representing a linearHex, no info is needed.\r\n info: !props.linearHex ? undefined : !isLinearMode ? ( // If representing a linear hex but we are in gammaMode, simple message explaining why linearHex is disabled\r\n <> This color picker is attached to an entity whose color is stored in gamma space, so we are showing linear hex in disabled view </>\r\n ) : (\r\n // If representing a linear hex and we are in linearMode, give information about how to use these hex values\r\n <>\r\n This color picker is attached to an entity whose color is stored in linear space (ex: PBR Material), and Babylon converts the color to gamma space\r\n before rendering on screen because the human eye is best at processing colors in gamma space. We thus also want to display the color picker in\r\n gamma space so that the color chosen here will match the color seen in your entity.\r\n <br />\r\n If you want to copy/paste the HEX into your code, you can either use\r\n <Body1Strong>Color3.FromHexString(LINEAR_HEX)</Body1Strong>\r\n <br />\r\n or\r\n <br />\r\n <Body1Strong>Color3.FromHexString(GAMMA_HEX).toLinearSpace()</Body1Strong>\r\n <br />\r\n <br />\r\n <Link url=\"https://doc.babylonjs.com/preparingArtForBabylon/controllingColorSpace/\" value=\"Read more in our docs!\" />\r\n </>\r\n ),\r\n }\r\n : undefined\r\n }\r\n />\r\n );\r\n};\r\n\r\ntype RgbKey = \"r\" | \"g\" | \"b\";\r\ntype InputRgbFieldProps = PrimitiveProps<Color3 | Color4> & {\r\n rgbKey: RgbKey;\r\n};\r\n\r\nconst InputRgbField: FunctionComponent<InputRgbFieldProps> = (props) => {\r\n const { value, onChange, title, rgbKey } = props;\r\n const classes = useColorPickerStyles();\r\n\r\n const handleChange = useCallback(\r\n (val: number) => {\r\n const newColor = value.clone();\r\n newColor[rgbKey] = val / 255.0; // Convert to 0-1 range\r\n onChange(newColor);\r\n },\r\n [value, onChange, rgbKey]\r\n );\r\n\r\n return (\r\n <SpinButton\r\n title={title}\r\n infoLabel={title ? { label: title } : undefined}\r\n className={classes.inputField}\r\n min={0}\r\n max={255}\r\n value={Math.round(value[rgbKey] * 255)}\r\n forceInt\r\n onChange={handleChange}\r\n />\r\n );\r\n};\r\n\r\nfunction rgbaToHsv(color: { r: number; g: number; b: number; a?: number }): { h: number; s: number; v: number; a?: number } {\r\n const c = new Color3(color.r, color.g, color.b);\r\n const hsv = c.toHSV();\r\n return { h: hsv.r, s: hsv.g, v: hsv.b, a: color.a };\r\n}\r\n\r\ntype HsvKey = \"h\" | \"s\" | \"v\";\r\ntype InputHsvFieldProps = PrimitiveProps<Color3 | Color4> & {\r\n hsvKey: HsvKey;\r\n max: number;\r\n scale?: number;\r\n};\r\n\r\n/**\r\n * In the HSV (Hue, Saturation, Value) color model, Hue (H) ranges from 0 to 360 degrees, representing the color's position on the color wheel.\r\n * Saturation (S) ranges from 0 to 100%, indicating the intensity or purity of the color, with 0 being shades of gray and 100 being a fully saturated color.\r\n * Value (V) ranges from 0 to 100%, representing the brightness of the color, with 0 being black and 100 being the brightest.\r\n * @param props - The properties for the InputHsvField component.\r\n */\r\nexport const InputHsvField: FunctionComponent<InputHsvFieldProps> = (props) => {\r\n const { value, title, hsvKey, max, onChange, scale = 1 } = props;\r\n\r\n const classes = useColorPickerStyles();\r\n\r\n const handleChange = useCallback(\r\n (val: number) => {\r\n // Convert current color to HSV, update the new hsv value, then call onChange prop\r\n const hsv = rgbaToHsv(value);\r\n hsv[hsvKey] = val / scale;\r\n let newColor: Color3 | Color4 = Color3.FromHSV(hsv.h, hsv.s, hsv.v);\r\n if (value instanceof Color4) {\r\n newColor = Color4.FromColor3(newColor, value.a ?? 1);\r\n }\r\n props.onChange(newColor);\r\n },\r\n [value, onChange, hsvKey, scale]\r\n );\r\n\r\n return (\r\n <SpinButton\r\n infoLabel={title ? { label: title } : undefined}\r\n title={title}\r\n className={classes.inputField}\r\n min={0}\r\n max={max}\r\n value={Math.round(rgbaToHsv(value)[hsvKey] * scale)}\r\n forceInt\r\n onChange={handleChange}\r\n />\r\n );\r\n};\r\n\r\ntype InputAlphaProps = {\r\n color: Color3 | Color4;\r\n onChange: (color: Color4) => void;\r\n};\r\n\r\n/**\r\n * Displays the alpha value of a color, either in the disabled state (if color is Color3) or as a spin button (if color is Color4).\r\n * @param props\r\n * @returns\r\n */\r\nconst InputAlphaField: FunctionComponent<InputAlphaProps> = (props) => {\r\n const classes = useColorPickerStyles();\r\n const { color, onChange } = props;\r\n\r\n const handleChange = useCallback(\r\n (value: number) => {\r\n if (Number.isNaN(value) || value < 0 || value > 1) {\r\n return;\r\n }\r\n\r\n if (color instanceof Color4) {\r\n const newColor = color.clone();\r\n newColor.a = value;\r\n return newColor;\r\n } else {\r\n return Color4.FromColor3(color, value);\r\n }\r\n },\r\n [onChange]\r\n );\r\n\r\n return (\r\n <SpinButton\r\n disabled={color instanceof Color3}\r\n min={0}\r\n max={1}\r\n className={classes.inputField}\r\n value={color instanceof Color3 ? 1 : color.a}\r\n step={0.01}\r\n onChange={handleChange}\r\n infoLabel={{\r\n label: \"Alpha\",\r\n info:\r\n color instanceof Color3 ? (\r\n <>\r\n Because this color picker is representing a Color3, we do not permit modifying alpha from the color picker. You can however modify the entity's alpha\r\n property directly, either in code via entity.alpha OR via inspector's transparency section.\r\n </>\r\n ) : undefined,\r\n }}\r\n />\r\n );\r\n};\r\n"]}
|
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
import type { FunctionComponent } from "react";
|
|
2
2
|
import type { PrimitiveProps } from "./primitive.js";
|
|
3
|
+
/**
|
|
4
|
+
* An option object for the ComboBox with separate label and value.
|
|
5
|
+
*/
|
|
6
|
+
export type ComboBoxOption = {
|
|
7
|
+
/**
|
|
8
|
+
* Defines the visible part of the option
|
|
9
|
+
*/
|
|
10
|
+
label: string;
|
|
11
|
+
/**
|
|
12
|
+
* Defines the value part of the option
|
|
13
|
+
*/
|
|
14
|
+
value: string;
|
|
15
|
+
};
|
|
3
16
|
export type ComboBoxProps = PrimitiveProps<string> & {
|
|
17
|
+
/**
|
|
18
|
+
* Label for the ComboBox
|
|
19
|
+
*/
|
|
4
20
|
label: string;
|
|
5
21
|
/**
|
|
6
|
-
*
|
|
22
|
+
* Options to display as label/value pairs
|
|
7
23
|
*/
|
|
8
|
-
options:
|
|
24
|
+
options: ComboBoxOption[];
|
|
9
25
|
};
|
|
10
26
|
/**
|
|
11
|
-
* Wrapper around a Fluent ComboBox that allows for filtering options
|
|
27
|
+
* Wrapper around a Fluent ComboBox that allows for filtering options.
|
|
12
28
|
* @param props
|
|
13
29
|
* @returns
|
|
14
30
|
*/
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useContext, useEffect } from "react";
|
|
3
|
-
import { Combobox as FluentComboBox, makeStyles, useComboboxFilter, useId } from "@fluentui/react-components";
|
|
3
|
+
import { Combobox as FluentComboBox, makeStyles, useComboboxFilter, useId, Option } from "@fluentui/react-components";
|
|
4
4
|
import { ToolContext } from "../hoc/fluentToolWrapper.js";
|
|
5
5
|
import { CustomTokens } from "./utils.js";
|
|
6
6
|
const useStyles = makeStyles({
|
|
7
7
|
root: {
|
|
8
|
-
// Stack the label above the field with a gap
|
|
9
8
|
display: "grid",
|
|
10
9
|
gridTemplateRows: "repeat(1fr)",
|
|
11
10
|
justifyItems: "start",
|
|
@@ -18,11 +17,11 @@ const useStyles = makeStyles({
|
|
|
18
17
|
boxSizing: "border-box",
|
|
19
18
|
},
|
|
20
19
|
input: {
|
|
21
|
-
minWidth: 0,
|
|
20
|
+
minWidth: 0,
|
|
22
21
|
},
|
|
23
22
|
});
|
|
24
23
|
/**
|
|
25
|
-
* Wrapper around a Fluent ComboBox that allows for filtering options
|
|
24
|
+
* Wrapper around a Fluent ComboBox that allows for filtering options.
|
|
26
25
|
* @param props
|
|
27
26
|
* @returns
|
|
28
27
|
*/
|
|
@@ -31,18 +30,24 @@ export const ComboBox = (props) => {
|
|
|
31
30
|
const comboId = useId();
|
|
32
31
|
const styles = useStyles();
|
|
33
32
|
const { size } = useContext(ToolContext);
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
// Find the label for the current value
|
|
34
|
+
const getLabel = (value) => props.options.find((opt) => opt.value === value)?.label ?? "";
|
|
35
|
+
const [query, setQuery] = useState(getLabel(props.value ?? ""));
|
|
36
36
|
useEffect(() => {
|
|
37
|
-
setQuery(props.value ?? "");
|
|
38
|
-
}, [props.value]);
|
|
39
|
-
|
|
37
|
+
setQuery(getLabel(props.value ?? ""));
|
|
38
|
+
}, [props.value, props.options]);
|
|
39
|
+
// Convert to Fluent's { children, value } format
|
|
40
|
+
const normalizedOptions = props.options.map((opt) => ({ children: opt.label, value: opt.value }));
|
|
41
|
+
const children = useComboboxFilter(query, normalizedOptions, {
|
|
40
42
|
noOptionsMessage: "No items match your search.",
|
|
43
|
+
optionToReactKey: (option) => option.value,
|
|
44
|
+
optionToText: (option) => option.children,
|
|
45
|
+
renderOption: (option) => (_jsx(Option, { value: option.value, text: option.children, children: option.children }, option.value)),
|
|
41
46
|
});
|
|
42
47
|
const onOptionSelect = (_e, data) => {
|
|
43
48
|
setQuery(data.optionText ?? "");
|
|
44
|
-
data.
|
|
49
|
+
data.optionValue && props.onChange(data.optionValue);
|
|
45
50
|
};
|
|
46
|
-
return (_jsxs("div", { className: styles.root, children: [_jsx("label", { id: comboId, children: props.label }), _jsx(FluentComboBox, { size: size, root: { className: styles.comboBox }, input: { className: styles.input }, onOptionSelect: onOptionSelect,
|
|
51
|
+
return (_jsxs("div", { className: styles.root, children: [_jsx("label", { id: comboId, children: props.label }), _jsx(FluentComboBox, { size: size, root: { className: styles.comboBox }, input: { className: styles.input }, onOptionSelect: onOptionSelect, "aria-labelledby": comboId, placeholder: "Search..", onChange: (ev) => setQuery(ev.target.value), value: query, children: children })] }));
|
|
47
52
|
};
|
|
48
53
|
//# sourceMappingURL=comboBox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comboBox.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/comboBox.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACxD,OAAO,EAAE,QAAQ,IAAI,cAAc,EAAE,UAAU,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"comboBox.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/comboBox.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { FunctionComponent } from \"react\";\r\nimport { useState, useContext, useEffect } from \"react\";\r\nimport { Combobox as FluentComboBox, makeStyles, useComboboxFilter, useId, Option } from \"@fluentui/react-components\";\r\nimport type { OptionOnSelectData, SelectionEvents } from \"@fluentui/react-components\";\r\nimport { ToolContext } from \"../hoc/fluentToolWrapper\";\r\nimport { CustomTokens } from \"./utils\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\n\r\nconst useStyles = makeStyles({\r\n root: {\r\n display: \"grid\",\r\n gridTemplateRows: \"repeat(1fr)\",\r\n justifyItems: \"start\",\r\n gap: \"2px\",\r\n maxWidth: \"400px\",\r\n },\r\n comboBox: {\r\n width: CustomTokens.inputWidth,\r\n minWidth: CustomTokens.inputWidth,\r\n boxSizing: \"border-box\",\r\n },\r\n input: {\r\n minWidth: 0,\r\n },\r\n});\r\n\r\n/**\r\n * An option object for the ComboBox with separate label and value.\r\n */\r\nexport type ComboBoxOption = {\r\n /**\r\n * Defines the visible part of the option\r\n */\r\n label: string;\r\n /**\r\n * Defines the value part of the option\r\n */\r\n value: string;\r\n};\r\n\r\nexport type ComboBoxProps = PrimitiveProps<string> & {\r\n /**\r\n * Label for the ComboBox\r\n */\r\n label: string;\r\n /**\r\n * Options to display as label/value pairs\r\n */\r\n options: ComboBoxOption[];\r\n};\r\n\r\n/**\r\n * Wrapper around a Fluent ComboBox that allows for filtering options.\r\n * @param props\r\n * @returns\r\n */\r\nexport const ComboBox: FunctionComponent<ComboBoxProps> = (props) => {\r\n ComboBox.displayName = \"ComboBox\";\r\n const comboId = useId();\r\n const styles = useStyles();\r\n const { size } = useContext(ToolContext);\r\n\r\n // Find the label for the current value\r\n const getLabel = (value: string) => props.options.find((opt) => opt.value === value)?.label ?? \"\";\r\n\r\n const [query, setQuery] = useState(getLabel(props.value ?? \"\"));\r\n\r\n useEffect(() => {\r\n setQuery(getLabel(props.value ?? \"\"));\r\n }, [props.value, props.options]);\r\n\r\n // Convert to Fluent's { children, value } format\r\n const normalizedOptions = props.options.map((opt) => ({ children: opt.label, value: opt.value }));\r\n\r\n const children = useComboboxFilter(query, normalizedOptions, {\r\n noOptionsMessage: \"No items match your search.\",\r\n optionToReactKey: (option) => option.value,\r\n optionToText: (option) => option.children,\r\n renderOption: (option) => (\r\n <Option key={option.value} value={option.value} text={option.children}>\r\n {option.children}\r\n </Option>\r\n ),\r\n });\r\n\r\n const onOptionSelect = (_e: SelectionEvents, data: OptionOnSelectData) => {\r\n setQuery(data.optionText ?? \"\");\r\n data.optionValue && props.onChange(data.optionValue);\r\n };\r\n\r\n return (\r\n <div className={styles.root}>\r\n <label id={comboId}>{props.label}</label>\r\n <FluentComboBox\r\n size={size}\r\n root={{ className: styles.comboBox }}\r\n input={{ className: styles.input }}\r\n onOptionSelect={onOptionSelect}\r\n aria-labelledby={comboId}\r\n placeholder=\"Search..\"\r\n onChange={(ev) => setQuery(ev.target.value)}\r\n value={query}\r\n >\r\n {children}\r\n </FluentComboBox>\r\n </div>\r\n );\r\n};\r\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Nullable } from "@onerjs/core/types.js";
|
|
2
|
+
import type { PrimitiveProps } from "./primitive.js";
|
|
3
|
+
type Entity = {
|
|
4
|
+
uniqueId: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Props for the EntitySelector component
|
|
8
|
+
*/
|
|
9
|
+
export type EntitySelectorProps<T extends Entity> = PrimitiveProps<Nullable<T>> & {
|
|
10
|
+
/**
|
|
11
|
+
* Function to get the list of entities to choose from
|
|
12
|
+
*/
|
|
13
|
+
getEntities: () => T[];
|
|
14
|
+
/**
|
|
15
|
+
* Function to get the display name from an entity
|
|
16
|
+
*/
|
|
17
|
+
getName: (entity: T) => string;
|
|
18
|
+
/**
|
|
19
|
+
* Optional filter function to filter which entities are shown
|
|
20
|
+
*/
|
|
21
|
+
filter?: (entity: T) => boolean;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* A generic primitive component with a ComboBox for selecting from a list of entities.
|
|
25
|
+
* Supports entities with duplicate names by using uniqueId for identity.
|
|
26
|
+
* @param props ChooseEntityProps
|
|
27
|
+
* @returns EntitySelector component
|
|
28
|
+
*/
|
|
29
|
+
export declare function EntitySelector<T extends Entity>(props: EntitySelectorProps<T>): JSX.Element;
|
|
30
|
+
export declare namespace EntitySelector {
|
|
31
|
+
var displayName: string;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { ComboBox } from "./comboBox.js";
|
|
4
|
+
/**
|
|
5
|
+
* A generic primitive component with a ComboBox for selecting from a list of entities.
|
|
6
|
+
* Supports entities with duplicate names by using uniqueId for identity.
|
|
7
|
+
* @param props ChooseEntityProps
|
|
8
|
+
* @returns EntitySelector component
|
|
9
|
+
*/
|
|
10
|
+
export function EntitySelector(props) {
|
|
11
|
+
const { value, onChange, getEntities, getName, filter } = props;
|
|
12
|
+
// Build options with uniqueId as key
|
|
13
|
+
const options = useMemo(() => {
|
|
14
|
+
return getEntities()
|
|
15
|
+
.filter((e) => e.uniqueId !== undefined && (!filter || filter(e)))
|
|
16
|
+
.map((entity) => ({
|
|
17
|
+
label: getName(entity),
|
|
18
|
+
value: entity.uniqueId.toString(),
|
|
19
|
+
}))
|
|
20
|
+
.sort((a, b) => a.label.localeCompare(b.label));
|
|
21
|
+
}, [getEntities, getName, filter]);
|
|
22
|
+
const handleEntitySelect = (key) => {
|
|
23
|
+
const entity = getEntities().find((e) => e.uniqueId.toString() === key);
|
|
24
|
+
onChange(entity ?? null);
|
|
25
|
+
};
|
|
26
|
+
// Get current entity key for display
|
|
27
|
+
const currentKey = value ? value.uniqueId.toString() : "";
|
|
28
|
+
return _jsx(ComboBox, { label: "", options: options, value: currentKey, onChange: handleEntitySelect });
|
|
29
|
+
}
|
|
30
|
+
EntitySelector.displayName = "EntitySelector";
|
|
31
|
+
//# sourceMappingURL=entitySelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitySelector.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/entitySelector.tsx"],"names":[],"mappings":";AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAqBtC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAmB,KAA6B;IAC1E,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAEhE,qCAAqC;IACrC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE;QACzB,OAAO,WAAW,EAAE;aACf,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;aACjE,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACd,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC;YACtB,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE;SACpC,CAAC,CAAC;aACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACxD,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAEnC,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAE,EAAE;QACvC,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,CAAC,CAAC;QACxE,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;IAC7B,CAAC,CAAC;IAEF,qCAAqC;IACrC,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAE1D,OAAO,KAAC,QAAQ,IAAC,KAAK,EAAC,EAAE,EAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,kBAAkB,GAAI,CAAC;AACpG,CAAC;AACD,cAAc,CAAC,WAAW,GAAG,gBAAgB,CAAC","sourcesContent":["import type { Nullable } from \"core/types\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\n\r\nimport { useMemo } from \"react\";\r\nimport { ComboBox } from \"./comboBox\";\r\n\r\ntype Entity = { uniqueId: number };\r\n/**\r\n * Props for the EntitySelector component\r\n */\r\nexport type EntitySelectorProps<T extends Entity> = PrimitiveProps<Nullable<T>> & {\r\n /**\r\n * Function to get the list of entities to choose from\r\n */\r\n getEntities: () => T[];\r\n /**\r\n * Function to get the display name from an entity\r\n */\r\n getName: (entity: T) => string;\r\n /**\r\n * Optional filter function to filter which entities are shown\r\n */\r\n filter?: (entity: T) => boolean;\r\n};\r\n\r\n/**\r\n * A generic primitive component with a ComboBox for selecting from a list of entities.\r\n * Supports entities with duplicate names by using uniqueId for identity.\r\n * @param props ChooseEntityProps\r\n * @returns EntitySelector component\r\n */\r\nexport function EntitySelector<T extends Entity>(props: EntitySelectorProps<T>): JSX.Element {\r\n const { value, onChange, getEntities, getName, filter } = props;\r\n\r\n // Build options with uniqueId as key\r\n const options = useMemo(() => {\r\n return getEntities()\r\n .filter((e) => e.uniqueId !== undefined && (!filter || filter(e)))\r\n .map((entity) => ({\r\n label: getName(entity),\r\n value: entity.uniqueId.toString(),\r\n }))\r\n .sort((a, b) => a.label.localeCompare(b.label));\r\n }, [getEntities, getName, filter]);\r\n\r\n const handleEntitySelect = (key: string) => {\r\n const entity = getEntities().find((e) => e.uniqueId.toString() === key);\r\n onChange(entity ?? null);\r\n };\r\n\r\n // Get current entity key for display\r\n const currentKey = value ? value.uniqueId.toString() : \"\";\r\n\r\n return <ComboBox label=\"\" options={options} value={currentKey} onChange={handleEntitySelect} />;\r\n}\r\nEntitySelector.displayName = \"EntitySelector\";\r\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FunctionComponent } from "react";
|
|
2
|
+
import type { Scene } from "@onerjs/core/scene.js";
|
|
3
|
+
import type { Nullable } from "@onerjs/core/types.js";
|
|
4
|
+
import type { Material } from "@onerjs/core/Materials/material.js";
|
|
5
|
+
import type { PrimitiveProps } from "./primitive.js";
|
|
6
|
+
export type MaterialSelectorProps = PrimitiveProps<Nullable<Material>> & {
|
|
7
|
+
/**
|
|
8
|
+
* The scene to get materials from
|
|
9
|
+
*/
|
|
10
|
+
scene: Scene;
|
|
11
|
+
/**
|
|
12
|
+
* Optional filter function to filter which materials are shown
|
|
13
|
+
*/
|
|
14
|
+
filter?: (material: Material) => boolean;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* A primitive component with a ComboBox for selecting from existing scene materials.
|
|
18
|
+
* @param props MaterialSelectorProps
|
|
19
|
+
* @returns MaterialSelector component
|
|
20
|
+
*/
|
|
21
|
+
export declare const MaterialSelector: FunctionComponent<MaterialSelectorProps>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { EntitySelector } from "./entitySelector.js";
|
|
4
|
+
/**
|
|
5
|
+
* A primitive component with a ComboBox for selecting from existing scene materials.
|
|
6
|
+
* @param props MaterialSelectorProps
|
|
7
|
+
* @returns MaterialSelector component
|
|
8
|
+
*/
|
|
9
|
+
export const MaterialSelector = (props) => {
|
|
10
|
+
MaterialSelector.displayName = "MaterialSelector";
|
|
11
|
+
const { scene, ...rest } = props;
|
|
12
|
+
const getMaterials = useCallback(() => scene.materials, [scene.materials]);
|
|
13
|
+
const getName = useCallback((material) => material.name, []);
|
|
14
|
+
return _jsx(EntitySelector, { ...rest, getEntities: getMaterials, getName: getName });
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=materialSelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materialSelector.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/materialSelector.tsx"],"names":[],"mappings":";AAMA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAalD;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA6C,CAAC,KAAK,EAAE,EAAE;IAChF,gBAAgB,CAAC,WAAW,GAAG,kBAAkB,CAAC;IAClD,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;IAEjC,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3E,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,QAAkB,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAEvE,OAAO,KAAC,cAAc,OAAK,IAAI,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAI,CAAC;AACrF,CAAC,CAAC","sourcesContent":["import type { FunctionComponent } from \"react\";\r\nimport type { Scene } from \"core/scene\";\r\nimport type { Nullable } from \"core/types\";\r\nimport type { Material } from \"core/Materials/material\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\n\r\nimport { useCallback } from \"react\";\r\nimport { EntitySelector } from \"./entitySelector\";\r\n\r\nexport type MaterialSelectorProps = PrimitiveProps<Nullable<Material>> & {\r\n /**\r\n * The scene to get materials from\r\n */\r\n scene: Scene;\r\n /**\r\n * Optional filter function to filter which materials are shown\r\n */\r\n filter?: (material: Material) => boolean;\r\n};\r\n\r\n/**\r\n * A primitive component with a ComboBox for selecting from existing scene materials.\r\n * @param props MaterialSelectorProps\r\n * @returns MaterialSelector component\r\n */\r\nexport const MaterialSelector: FunctionComponent<MaterialSelectorProps> = (props) => {\r\n MaterialSelector.displayName = \"MaterialSelector\";\r\n const { scene, ...rest } = props;\r\n\r\n const getMaterials = useCallback(() => scene.materials, [scene.materials]);\r\n const getName = useCallback((material: Material) => material.name, []);\r\n\r\n return <EntitySelector {...rest} getEntities={getMaterials} getName={getName} />;\r\n};\r\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FunctionComponent } from "react";
|
|
2
|
+
import type { Scene } from "@onerjs/core/scene.js";
|
|
3
|
+
import type { Nullable } from "@onerjs/core/types.js";
|
|
4
|
+
import type { Node } from "@onerjs/core/node.js";
|
|
5
|
+
import type { PrimitiveProps } from "./primitive.js";
|
|
6
|
+
export type NodeSelectorProps = PrimitiveProps<Nullable<Node>> & {
|
|
7
|
+
/**
|
|
8
|
+
* The scene to get nodes from
|
|
9
|
+
*/
|
|
10
|
+
scene: Scene;
|
|
11
|
+
/**
|
|
12
|
+
* Optional filter function to filter which nodes are shown
|
|
13
|
+
*/
|
|
14
|
+
filter?: (node: Node) => boolean;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* A primitive component with a ComboBox for selecting from existing scene nodes.
|
|
18
|
+
* @param props NodeSelectorProps
|
|
19
|
+
* @returns NodeSelector component
|
|
20
|
+
*/
|
|
21
|
+
export declare const NodeSelector: FunctionComponent<NodeSelectorProps>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { EntitySelector } from "./entitySelector.js";
|
|
4
|
+
/**
|
|
5
|
+
* A primitive component with a ComboBox for selecting from existing scene nodes.
|
|
6
|
+
* @param props NodeSelectorProps
|
|
7
|
+
* @returns NodeSelector component
|
|
8
|
+
*/
|
|
9
|
+
export const NodeSelector = (props) => {
|
|
10
|
+
NodeSelector.displayName = "NodeSelector";
|
|
11
|
+
const { scene, ...rest } = props;
|
|
12
|
+
const getNodes = useCallback(() => scene.getNodes(), [scene]);
|
|
13
|
+
const getName = useCallback((node) => node.name, []);
|
|
14
|
+
return _jsx(EntitySelector, { ...rest, getEntities: getNodes, getName: getName });
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=nodeSelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodeSelector.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/nodeSelector.tsx"],"names":[],"mappings":";AAMA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAalD;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAyC,CAAC,KAAK,EAAE,EAAE;IACxE,YAAY,CAAC,WAAW,GAAG,cAAc,CAAC;IAC1C,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;IAEjC,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9D,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAU,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAE3D,OAAO,KAAC,cAAc,OAAK,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAI,CAAC;AACjF,CAAC,CAAC","sourcesContent":["import type { FunctionComponent } from \"react\";\r\nimport type { Scene } from \"core/scene\";\r\nimport type { Nullable } from \"core/types\";\r\nimport type { Node } from \"core/node\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\n\r\nimport { useCallback } from \"react\";\r\nimport { EntitySelector } from \"./entitySelector\";\r\n\r\nexport type NodeSelectorProps = PrimitiveProps<Nullable<Node>> & {\r\n /**\r\n * The scene to get nodes from\r\n */\r\n scene: Scene;\r\n /**\r\n * Optional filter function to filter which nodes are shown\r\n */\r\n filter?: (node: Node) => boolean;\r\n};\r\n\r\n/**\r\n * A primitive component with a ComboBox for selecting from existing scene nodes.\r\n * @param props NodeSelectorProps\r\n * @returns NodeSelector component\r\n */\r\nexport const NodeSelector: FunctionComponent<NodeSelectorProps> = (props) => {\r\n NodeSelector.displayName = \"NodeSelector\";\r\n const { scene, ...rest } = props;\r\n\r\n const getNodes = useCallback(() => scene.getNodes(), [scene]);\r\n const getName = useCallback((node: Node) => node.name, []);\r\n\r\n return <EntitySelector {...rest} getEntities={getNodes} getName={getName} />;\r\n};\r\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PropsWithChildren, ReactElement } from "react";
|
|
2
2
|
import type { FluentIcon } from "@fluentui/react-icons";
|
|
3
3
|
type PopoverWithIconProps = {
|
|
4
4
|
icon: FluentIcon;
|
|
@@ -9,5 +9,5 @@ type PopoverWithTriggerProps = {
|
|
|
9
9
|
trigger: ReactElement;
|
|
10
10
|
};
|
|
11
11
|
type PopoverProps = PopoverWithIconProps | PopoverWithTriggerProps;
|
|
12
|
-
export declare const Popover:
|
|
12
|
+
export declare const Popover: import("react").ForwardRefExoticComponent<PropsWithChildren<PopoverProps> & import("react").RefAttributes<HTMLButtonElement>>;
|
|
13
13
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { forwardRef, useState } from "react";
|
|
3
3
|
import { Popover as FluentPopover, PopoverTrigger, PopoverSurface, makeStyles, tokens } from "@fluentui/react-components";
|
|
4
4
|
import { Button } from "shared-ui-components/fluent/primitives/button";
|
|
5
5
|
const useStyles = makeStyles({
|
|
@@ -14,7 +14,7 @@ const useStyles = makeStyles({
|
|
|
14
14
|
minWidth: "300px",
|
|
15
15
|
},
|
|
16
16
|
});
|
|
17
|
-
export const Popover = (props) => {
|
|
17
|
+
export const Popover = forwardRef((props, ref) => {
|
|
18
18
|
const { children } = props;
|
|
19
19
|
const [popoverOpen, setPopoverOpen] = useState(false);
|
|
20
20
|
const classes = useStyles();
|
|
@@ -22,6 +22,6 @@ export const Popover = (props) => {
|
|
|
22
22
|
align: "start",
|
|
23
23
|
overflowBoundary: document.body,
|
|
24
24
|
autoSize: true,
|
|
25
|
-
}, trapFocus: true, children: [_jsx(PopoverTrigger, { disableButtonEnhancement: true, children: props.trigger ?? _jsx(Button, { icon: props.icon, onClick: () => setPopoverOpen(true) }) }), _jsx(PopoverSurface, { className: classes.surface, children: _jsx("div", { className: classes.content, children: children }) })] }));
|
|
26
|
-
};
|
|
25
|
+
}, trapFocus: true, children: [_jsx(PopoverTrigger, { disableButtonEnhancement: true, children: props.trigger ?? _jsx(Button, { ref: ref, icon: props.icon, onClick: () => setPopoverOpen(true) }) }), _jsx(PopoverSurface, { className: classes.surface, children: _jsx("div", { className: classes.content, children: children }) })] }));
|
|
26
|
+
});
|
|
27
27
|
//# sourceMappingURL=popover.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/popover.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"popover.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/popover.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAE1H,OAAO,EAAE,MAAM,EAAE,MAAM,+CAA+C,CAAC;AAEvE,MAAM,SAAS,GAAG,UAAU,CAAC;IACzB,OAAO,EAAE;QACL,QAAQ,EAAE,OAAO;KACpB;IACD,OAAO,EAAE;QACL,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,QAAQ;QACvB,GAAG,EAAE,MAAM,CAAC,gBAAgB;QAC5B,OAAO,EAAE,MAAM,CAAC,kBAAkB;QAClC,QAAQ,EAAE,OAAO;KACpB;CACJ,CAAC,CAAC;AAcH,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAAqD,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IACjG,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAC3B,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;IAE5B,OAAO,CACH,MAAC,aAAa,IACV,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EACpD,WAAW,EAAE;YACT,KAAK,EAAE,OAAO;YACd,gBAAgB,EAAE,QAAQ,CAAC,IAAI;YAC/B,QAAQ,EAAE,IAAI;SACjB,EACD,SAAS,mBAET,KAAC,cAAc,IAAC,wBAAwB,kBAAE,KAAK,CAAC,OAAO,IAAI,KAAC,MAAM,IAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,GAAI,GAAkB,EACxJ,KAAC,cAAc,IAAC,SAAS,EAAE,OAAO,CAAC,OAAO,YACtC,cAAK,SAAS,EAAE,OAAO,CAAC,OAAO,YAAG,QAAQ,GAAO,GACpC,IACL,CACnB,CAAC;AACN,CAAC,CAAC,CAAC","sourcesContent":["import type { PropsWithChildren, ReactElement } from \"react\";\r\nimport { forwardRef, useState } from \"react\";\r\nimport { Popover as FluentPopover, PopoverTrigger, PopoverSurface, makeStyles, tokens } from \"@fluentui/react-components\";\r\nimport type { FluentIcon } from \"@fluentui/react-icons\";\r\nimport { Button } from \"shared-ui-components/fluent/primitives/button\";\r\n\r\nconst useStyles = makeStyles({\r\n surface: {\r\n maxWidth: \"400px\",\r\n },\r\n content: {\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: tokens.spacingVerticalM,\r\n padding: tokens.spacingHorizontalL,\r\n minWidth: \"300px\",\r\n },\r\n});\r\n\r\ntype PopoverWithIconProps = {\r\n icon: FluentIcon;\r\n trigger?: never;\r\n};\r\n\r\ntype PopoverWithTriggerProps = {\r\n icon?: never;\r\n trigger: ReactElement;\r\n};\r\n\r\ntype PopoverProps = PopoverWithIconProps | PopoverWithTriggerProps;\r\n\r\nexport const Popover = forwardRef<HTMLButtonElement, PropsWithChildren<PopoverProps>>((props, ref) => {\r\n const { children } = props;\r\n const [popoverOpen, setPopoverOpen] = useState(false);\r\n const classes = useStyles();\r\n\r\n return (\r\n <FluentPopover\r\n open={popoverOpen}\r\n onOpenChange={(_, data) => setPopoverOpen(data.open)}\r\n positioning={{\r\n align: \"start\",\r\n overflowBoundary: document.body,\r\n autoSize: true,\r\n }}\r\n trapFocus\r\n >\r\n <PopoverTrigger disableButtonEnhancement>{props.trigger ?? <Button ref={ref} icon={props.icon} onClick={() => setPopoverOpen(true)} />}</PopoverTrigger>\r\n <PopoverSurface className={classes.surface}>\r\n <div className={classes.content}>{children}</div>\r\n </PopoverSurface>\r\n </FluentPopover>\r\n );\r\n});\r\n"]}
|
|
@@ -3,7 +3,7 @@ import type { Scene } from "@onerjs/core/scene.js";
|
|
|
3
3
|
import type { Nullable } from "@onerjs/core/types.js";
|
|
4
4
|
import type { FunctionComponent } from "react";
|
|
5
5
|
import type { PrimitiveProps } from "./primitive.js";
|
|
6
|
-
export type
|
|
6
|
+
export type TextureSelectorProps = PrimitiveProps<Nullable<BaseTexture>> & {
|
|
7
7
|
/**
|
|
8
8
|
* The scene to get textures from
|
|
9
9
|
*/
|
|
@@ -20,7 +20,7 @@ export type ChooseTextureProps = PrimitiveProps<Nullable<BaseTexture>> & {
|
|
|
20
20
|
/**
|
|
21
21
|
* A primitive component with a ComboBox for selecting from existing scene textures
|
|
22
22
|
* and a button for uploading new texture files.
|
|
23
|
-
* @param props
|
|
24
|
-
* @returns
|
|
23
|
+
* @param props TextureSelectorProps
|
|
24
|
+
* @returns TextureSelector component
|
|
25
25
|
*/
|
|
26
|
-
export declare const
|
|
26
|
+
export declare const TextureSelector: FunctionComponent<TextureSelectorProps>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { makeStyles, tokens } from "@fluentui/react-components";
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
import { TextureUpload } from "../hoc/textureUpload.js";
|
|
5
|
+
import { EntitySelector } from "./entitySelector.js";
|
|
6
|
+
const useStyles = makeStyles({
|
|
7
|
+
container: {
|
|
8
|
+
display: "flex",
|
|
9
|
+
flexDirection: "row",
|
|
10
|
+
alignItems: "center",
|
|
11
|
+
gap: tokens.spacingHorizontalS,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* A primitive component with a ComboBox for selecting from existing scene textures
|
|
16
|
+
* and a button for uploading new texture files.
|
|
17
|
+
* @param props TextureSelectorProps
|
|
18
|
+
* @returns TextureSelector component
|
|
19
|
+
*/
|
|
20
|
+
export const TextureSelector = (props) => {
|
|
21
|
+
TextureSelector.displayName = "TextureSelector";
|
|
22
|
+
const { scene, cubeOnly, value, onChange } = props;
|
|
23
|
+
const classes = useStyles();
|
|
24
|
+
const getTextures = useCallback(() => scene.textures, [scene.textures]);
|
|
25
|
+
const getName = useCallback((texture) => texture.displayName || texture.name, []);
|
|
26
|
+
const filter = useCallback((texture) => !cubeOnly || texture.isCube, [cubeOnly]);
|
|
27
|
+
return (_jsxs("div", { className: classes.container, children: [_jsx(EntitySelector, { value: value, onChange: onChange, getEntities: getTextures, getName: getName, filter: filter }), _jsx(TextureUpload, { scene: scene, onChange: onChange, cubeOnly: cubeOnly })] }));
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=textureSelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textureSelector.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/textureSelector.tsx"],"names":[],"mappings":";AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,MAAM,SAAS,GAAG,UAAU,CAAC;IACzB,SAAS,EAAE;QACP,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,QAAQ;QACpB,GAAG,EAAE,MAAM,CAAC,kBAAkB;KACjC;CACJ,CAAC,CAAC;AAiBH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAA4C,CAAC,KAAK,EAAE,EAAE;IAC9E,eAAe,CAAC,WAAW,GAAG,iBAAiB,CAAC;IAChD,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IACnD,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;IAE5B,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,OAAoB,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC/F,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,OAAoB,EAAE,EAAE,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAE9F,OAAO,CACH,eAAK,SAAS,EAAE,OAAO,CAAC,SAAS,aAC7B,KAAC,cAAc,IAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAI,EAChH,KAAC,aAAa,IAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAI,IACrE,CACT,CAAC;AACN,CAAC,CAAC","sourcesContent":["import type { BaseTexture } from \"core/Materials/Textures/baseTexture\";\r\nimport type { Scene } from \"core/scene\";\r\nimport type { Nullable } from \"core/types\";\r\nimport type { FunctionComponent } from \"react\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\n\r\nimport { makeStyles, tokens } from \"@fluentui/react-components\";\r\nimport { useCallback } from \"react\";\r\nimport { TextureUpload } from \"../hoc/textureUpload\";\r\nimport { EntitySelector } from \"./entitySelector\";\r\n\r\nconst useStyles = makeStyles({\r\n container: {\r\n display: \"flex\",\r\n flexDirection: \"row\",\r\n alignItems: \"center\",\r\n gap: tokens.spacingHorizontalS,\r\n },\r\n});\r\n\r\nexport type TextureSelectorProps = PrimitiveProps<Nullable<BaseTexture>> & {\r\n /**\r\n * The scene to get textures from\r\n */\r\n scene: Scene;\r\n /**\r\n * File types to accept for upload\r\n */\r\n accept?: string;\r\n /**\r\n * Whether to only allow cube textures\r\n */\r\n cubeOnly?: boolean;\r\n};\r\n\r\n/**\r\n * A primitive component with a ComboBox for selecting from existing scene textures\r\n * and a button for uploading new texture files.\r\n * @param props TextureSelectorProps\r\n * @returns TextureSelector component\r\n */\r\nexport const TextureSelector: FunctionComponent<TextureSelectorProps> = (props) => {\r\n TextureSelector.displayName = \"TextureSelector\";\r\n const { scene, cubeOnly, value, onChange } = props;\r\n const classes = useStyles();\r\n\r\n const getTextures = useCallback(() => scene.textures, [scene.textures]);\r\n const getName = useCallback((texture: BaseTexture) => texture.displayName || texture.name, []);\r\n const filter = useCallback((texture: BaseTexture) => !cubeOnly || texture.isCube, [cubeOnly]);\r\n\r\n return (\r\n <div className={classes.container}>\r\n <EntitySelector value={value} onChange={onChange} getEntities={getTextures} getName={getName} filter={filter} />\r\n <TextureUpload scene={scene} onChange={onChange} cubeOnly={cubeOnly} />\r\n </div>\r\n );\r\n};\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { FunctionComponent } from "react";
|
|
2
|
-
import type { BaseTexture } from "@onerjs/core/Materials/Textures/baseTexture.js";
|
|
3
|
-
import type { Nullable } from "@onerjs/core/types.js";
|
|
4
|
-
import type { PropertyLineProps } from "./propertyLine.js";
|
|
5
|
-
import type { ChooseTextureProps } from "../../primitives/chooseTexture.js";
|
|
6
|
-
type ChooseTexturePropertyLineProps = PropertyLineProps<Nullable<BaseTexture>> & ChooseTextureProps;
|
|
7
|
-
/**
|
|
8
|
-
* A property line with a ComboBox for selecting from existing scene textures
|
|
9
|
-
* and a button for uploading new texture files.
|
|
10
|
-
* @param props - ChooseTextureProps & PropertyLineProps
|
|
11
|
-
* @returns property-line wrapped ChooseTexture component
|
|
12
|
-
*/
|
|
13
|
-
export declare const ChooseTexturePropertyLine: FunctionComponent<ChooseTexturePropertyLineProps>;
|
|
14
|
-
export {};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { PropertyLine } from "./propertyLine.js";
|
|
3
|
-
import { ChooseTexture } from "../../primitives/chooseTexture.js";
|
|
4
|
-
/**
|
|
5
|
-
* A property line with a ComboBox for selecting from existing scene textures
|
|
6
|
-
* and a button for uploading new texture files.
|
|
7
|
-
* @param props - ChooseTextureProps & PropertyLineProps
|
|
8
|
-
* @returns property-line wrapped ChooseTexture component
|
|
9
|
-
*/
|
|
10
|
-
export const ChooseTexturePropertyLine = (props) => {
|
|
11
|
-
ChooseTexturePropertyLine.displayName = "ChooseTexturePropertyLine";
|
|
12
|
-
return (_jsx(PropertyLine, { ...props, children: _jsx(ChooseTexture, { ...props }) }));
|
|
13
|
-
};
|
|
14
|
-
//# sourceMappingURL=chooseTexturePropertyLine.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chooseTexturePropertyLine.js","sourceRoot":"","sources":["../../../../../../dev/sharedUiComponents/src/fluent/hoc/propertyLines/chooseTexturePropertyLine.tsx"],"names":[],"mappings":";AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAI/D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAsD,CAAC,KAAK,EAAE,EAAE;IAClG,yBAAyB,CAAC,WAAW,GAAG,2BAA2B,CAAC;IAEpE,OAAO,CACH,KAAC,YAAY,OAAK,KAAK,YACnB,KAAC,aAAa,OAAK,KAAK,GAAI,GACjB,CAClB,CAAC;AACN,CAAC,CAAC","sourcesContent":["import type { FunctionComponent } from \"react\";\r\nimport type { BaseTexture } from \"core/Materials/Textures/baseTexture\";\r\nimport type { Nullable } from \"core/types\";\r\nimport type { PropertyLineProps } from \"./propertyLine\";\r\nimport type { ChooseTextureProps } from \"../../primitives/chooseTexture\";\r\n\r\nimport { PropertyLine } from \"./propertyLine\";\r\nimport { ChooseTexture } from \"../../primitives/chooseTexture\";\r\n\r\ntype ChooseTexturePropertyLineProps = PropertyLineProps<Nullable<BaseTexture>> & ChooseTextureProps;\r\n\r\n/**\r\n * A property line with a ComboBox for selecting from existing scene textures\r\n * and a button for uploading new texture files.\r\n * @param props - ChooseTextureProps & PropertyLineProps\r\n * @returns property-line wrapped ChooseTexture component\r\n */\r\nexport const ChooseTexturePropertyLine: FunctionComponent<ChooseTexturePropertyLineProps> = (props) => {\r\n ChooseTexturePropertyLine.displayName = \"ChooseTexturePropertyLine\";\r\n\r\n return (\r\n <PropertyLine {...props}>\r\n <ChooseTexture {...props} />\r\n </PropertyLine>\r\n );\r\n};\r\n"]}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { makeStyles, tokens } from "@fluentui/react-components";
|
|
3
|
-
import { useMemo } from "react";
|
|
4
|
-
import { TextureUpload } from "../hoc/textureUpload.js";
|
|
5
|
-
import { ComboBox } from "./comboBox.js";
|
|
6
|
-
const useStyles = makeStyles({
|
|
7
|
-
container: {
|
|
8
|
-
display: "flex",
|
|
9
|
-
flexDirection: "row",
|
|
10
|
-
alignItems: "center",
|
|
11
|
-
gap: tokens.spacingHorizontalS,
|
|
12
|
-
},
|
|
13
|
-
});
|
|
14
|
-
/**
|
|
15
|
-
* A primitive component with a ComboBox for selecting from existing scene textures
|
|
16
|
-
* and a button for uploading new texture files.
|
|
17
|
-
* @param props ChooseTextureProps
|
|
18
|
-
* @returns ChooseTexture component
|
|
19
|
-
*/
|
|
20
|
-
export const ChooseTexture = (props) => {
|
|
21
|
-
ChooseTexture.displayName = "ChooseTexture";
|
|
22
|
-
const { scene, cubeOnly, value, onChange } = props;
|
|
23
|
-
const classes = useStyles();
|
|
24
|
-
// Get sorted texture names from scene
|
|
25
|
-
const textureOptions = useMemo(() => {
|
|
26
|
-
return scene.textures
|
|
27
|
-
.filter((t) => t.name && (!cubeOnly || t.isCube))
|
|
28
|
-
.map((t) => t.displayName || t.name)
|
|
29
|
-
.sort((a, b) => a.localeCompare(b));
|
|
30
|
-
}, [scene.textures, cubeOnly]);
|
|
31
|
-
const handleTextureSelect = (textureName) => {
|
|
32
|
-
const texture = scene.textures.find((t) => (t.displayName || t.name) === textureName);
|
|
33
|
-
onChange(texture ?? null);
|
|
34
|
-
};
|
|
35
|
-
// Get current texture name for initial display
|
|
36
|
-
const currentTextureName = value ? value.displayName || value.name : "";
|
|
37
|
-
return (_jsxs("div", { className: classes.container, children: [_jsx(ComboBox, { label: "", options: textureOptions, value: currentTextureName, onChange: handleTextureSelect }), _jsx(TextureUpload, { scene: scene, onChange: onChange, cubeOnly: cubeOnly })] }));
|
|
38
|
-
};
|
|
39
|
-
//# sourceMappingURL=chooseTexture.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chooseTexture.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/chooseTexture.tsx"],"names":[],"mappings":";AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,SAAS,GAAG,UAAU,CAAC;IACzB,SAAS,EAAE;QACP,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,QAAQ;QACpB,GAAG,EAAE,MAAM,CAAC,kBAAkB;KACjC;CACJ,CAAC,CAAC;AAiBH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAA0C,CAAC,KAAK,EAAE,EAAE;IAC1E,aAAa,CAAC,WAAW,GAAG,eAAe,CAAC;IAC5C,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IACnD,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;IAE5B,sCAAsC;IACtC,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,EAAE;QAChC,OAAO,KAAK,CAAC,QAAQ;aAChB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;aAChD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,CAAC;aACnC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE/B,MAAM,mBAAmB,GAAG,CAAC,WAAmB,EAAE,EAAE;QAChD,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,WAAW,CAAC,CAAC;QACtF,QAAQ,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC;IAEF,+CAA+C;IAC/C,MAAM,kBAAkB,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAExE,OAAO,CACH,eAAK,SAAS,EAAE,OAAO,CAAC,SAAS,aAC7B,KAAC,QAAQ,IAAC,KAAK,EAAC,EAAE,EAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,mBAAmB,GAAI,EACxG,KAAC,aAAa,IAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAI,IACrE,CACT,CAAC;AACN,CAAC,CAAC","sourcesContent":["import type { BaseTexture } from \"core/Materials/Textures/baseTexture\";\r\nimport type { Scene } from \"core/scene\";\r\nimport type { Nullable } from \"core/types\";\r\nimport type { FunctionComponent } from \"react\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\n\r\nimport { makeStyles, tokens } from \"@fluentui/react-components\";\r\nimport { useMemo } from \"react\";\r\nimport { TextureUpload } from \"../hoc/textureUpload\";\r\nimport { ComboBox } from \"./comboBox\";\r\n\r\nconst useStyles = makeStyles({\r\n container: {\r\n display: \"flex\",\r\n flexDirection: \"row\",\r\n alignItems: \"center\",\r\n gap: tokens.spacingHorizontalS,\r\n },\r\n});\r\n\r\nexport type ChooseTextureProps = PrimitiveProps<Nullable<BaseTexture>> & {\r\n /**\r\n * The scene to get textures from\r\n */\r\n scene: Scene;\r\n /**\r\n * File types to accept for upload\r\n */\r\n accept?: string;\r\n /**\r\n * Whether to only allow cube textures\r\n */\r\n cubeOnly?: boolean;\r\n};\r\n\r\n/**\r\n * A primitive component with a ComboBox for selecting from existing scene textures\r\n * and a button for uploading new texture files.\r\n * @param props ChooseTextureProps\r\n * @returns ChooseTexture component\r\n */\r\nexport const ChooseTexture: FunctionComponent<ChooseTextureProps> = (props) => {\r\n ChooseTexture.displayName = \"ChooseTexture\";\r\n const { scene, cubeOnly, value, onChange } = props;\r\n const classes = useStyles();\r\n\r\n // Get sorted texture names from scene\r\n const textureOptions = useMemo(() => {\r\n return scene.textures\r\n .filter((t) => t.name && (!cubeOnly || t.isCube))\r\n .map((t) => t.displayName || t.name)\r\n .sort((a, b) => a.localeCompare(b));\r\n }, [scene.textures, cubeOnly]);\r\n\r\n const handleTextureSelect = (textureName: string) => {\r\n const texture = scene.textures.find((t) => (t.displayName || t.name) === textureName);\r\n onChange(texture ?? null);\r\n };\r\n\r\n // Get current texture name for initial display\r\n const currentTextureName = value ? value.displayName || value.name : \"\";\r\n\r\n return (\r\n <div className={classes.container}>\r\n <ComboBox label=\"\" options={textureOptions} value={currentTextureName} onChange={handleTextureSelect} />\r\n <TextureUpload scene={scene} onChange={onChange} cubeOnly={cubeOnly} />\r\n </div>\r\n );\r\n};\r\n"]}
|