@babylonjs/shared-ui-components 8.41.2 → 8.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fluent/hoc/fileUploadLine.d.ts +5 -0
- package/fluent/hoc/fileUploadLine.js +10 -17
- package/fluent/hoc/fileUploadLine.js.map +1 -1
- package/fluent/hoc/propertyLines/chooseTexturePropertyLine.d.ts +14 -0
- package/fluent/hoc/propertyLines/chooseTexturePropertyLine.js +14 -0
- package/fluent/hoc/propertyLines/chooseTexturePropertyLine.js.map +1 -0
- package/fluent/hoc/propertyLines/comboBoxPropertyLine.d.ts +11 -0
- package/fluent/hoc/propertyLines/comboBoxPropertyLine.js +13 -0
- package/fluent/hoc/propertyLines/comboBoxPropertyLine.js.map +1 -0
- package/fluent/hoc/textureUpload.d.ts +40 -0
- package/fluent/hoc/textureUpload.js +64 -0
- package/fluent/hoc/textureUpload.js.map +1 -0
- package/fluent/primitives/button.d.ts +7 -3
- package/fluent/primitives/button.js +5 -5
- package/fluent/primitives/button.js.map +1 -1
- package/fluent/primitives/chooseTexture.d.ts +26 -0
- package/fluent/primitives/chooseTexture.js +39 -0
- package/fluent/primitives/chooseTexture.js.map +1 -0
- package/fluent/primitives/colorPicker.js +15 -19
- package/fluent/primitives/colorPicker.js.map +1 -1
- package/fluent/primitives/comboBox.d.ts +6 -3
- package/fluent/primitives/comboBox.js +19 -4
- package/fluent/primitives/comboBox.js.map +1 -1
- package/fluent/primitives/popover.d.ts +13 -0
- package/fluent/primitives/popover.js +27 -0
- package/fluent/primitives/popover.js.map +1 -0
- package/fluent/primitives/uploadButton.d.ts +24 -0
- package/fluent/primitives/uploadButton.js +27 -0
- package/fluent/primitives/uploadButton.js.map +1 -0
- package/package.json +1 -1
|
@@ -5,5 +5,10 @@ type FileUploadLineProps = Omit<ButtonProps, "onClick" | "label"> & {
|
|
|
5
5
|
label: string;
|
|
6
6
|
accept: string;
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* A full-width line with an upload button.
|
|
10
|
+
* For just the button without the line wrapper, use UploadButton directly.
|
|
11
|
+
* @returns An UploadButton wrapped in a LineContainer
|
|
12
|
+
*/
|
|
8
13
|
export declare const FileUploadLine: FunctionComponent<FileUploadLineProps>;
|
|
9
14
|
export {};
|
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { LineContainer } from "./propertyLines/propertyLine.js";
|
|
3
|
+
import { UploadButton } from "../primitives/uploadButton.js";
|
|
4
|
+
/**
|
|
5
|
+
* A full-width line with an upload button.
|
|
6
|
+
* For just the button without the line wrapper, use UploadButton directly.
|
|
7
|
+
* @returns An UploadButton wrapped in a LineContainer
|
|
8
|
+
*/
|
|
9
|
+
export const FileUploadLine = ({ onClick, label, accept, ...buttonProps }) => {
|
|
6
10
|
FileUploadLine.displayName = "FileUploadLine";
|
|
7
|
-
|
|
8
|
-
const handleButtonClick = () => {
|
|
9
|
-
inputRef.current?.click();
|
|
10
|
-
};
|
|
11
|
-
const handleChange = (evt) => {
|
|
12
|
-
const files = evt.target.files;
|
|
13
|
-
if (files && files.length) {
|
|
14
|
-
props.onClick(files);
|
|
15
|
-
}
|
|
16
|
-
evt.target.value = "";
|
|
17
|
-
};
|
|
18
|
-
return (_jsxs(_Fragment, { children: [_jsx(ButtonLine, { onClick: handleButtonClick, icon: ArrowUploadRegular, label: props.label }), _jsx("input", { ref: inputRef, type: "file", accept: props.accept, style: { display: "none" }, onChange: handleChange })] }));
|
|
11
|
+
return (_jsx(LineContainer, { children: _jsx(UploadButton, { onUpload: onClick, accept: accept, label: label, ...buttonProps }) }));
|
|
19
12
|
};
|
|
20
13
|
//# sourceMappingURL=fileUploadLine.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileUploadLine.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/hoc/fileUploadLine.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"fileUploadLine.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/hoc/fileUploadLine.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAS1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAA2C,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,EAAE,EAAE;IACjH,cAAc,CAAC,WAAW,GAAG,gBAAgB,CAAC;IAE9C,OAAO,CACH,KAAC,aAAa,cACV,KAAC,YAAY,IAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAM,WAAW,GAAI,GACtE,CACnB,CAAC;AACN,CAAC,CAAC","sourcesContent":["import type { FunctionComponent } from \"react\";\r\nimport { LineContainer } from \"./propertyLines/propertyLine\";\r\nimport { UploadButton } from \"../primitives/uploadButton\";\r\nimport type { ButtonProps } from \"../primitives/button\";\r\n\r\ntype FileUploadLineProps = Omit<ButtonProps, \"onClick\" | \"label\"> & {\r\n onClick: (files: FileList) => void;\r\n label: string; // Require a label when button is the entire line (by default, label is optional on an UploadButton\r\n accept: string;\r\n};\r\n\r\n/**\r\n * A full-width line with an upload button.\r\n * For just the button without the line wrapper, use UploadButton directly.\r\n * @returns An UploadButton wrapped in a LineContainer\r\n */\r\nexport const FileUploadLine: FunctionComponent<FileUploadLineProps> = ({ onClick, label, accept, ...buttonProps }) => {\r\n FileUploadLine.displayName = \"FileUploadLine\";\r\n\r\n return (\r\n <LineContainer>\r\n <UploadButton onUpload={onClick} accept={accept} label={label} {...buttonProps} />\r\n </LineContainer>\r\n );\r\n};\r\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FunctionComponent } from "react";
|
|
2
|
+
import type { BaseTexture } from "@babylonjs/core/Materials/Textures/baseTexture.js";
|
|
3
|
+
import type { Nullable } from "@babylonjs/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 {};
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FunctionComponent } from "react";
|
|
2
|
+
import type { PropertyLineProps } from "./propertyLine.js";
|
|
3
|
+
import type { ComboBoxProps } from "../../primitives/comboBox.js";
|
|
4
|
+
type ComboBoxPropertyLineProps = ComboBoxProps & PropertyLineProps<string>;
|
|
5
|
+
/**
|
|
6
|
+
* A property line with a filterable ComboBox
|
|
7
|
+
* @param props - ComboBoxProps & PropertyLineProps
|
|
8
|
+
* @returns property-line wrapped ComboBox component
|
|
9
|
+
*/
|
|
10
|
+
export declare const ComboBoxPropertyLine: FunctionComponent<ComboBoxPropertyLineProps>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { PropertyLine } from "./propertyLine.js";
|
|
3
|
+
import { ComboBox } from "../../primitives/comboBox.js";
|
|
4
|
+
/**
|
|
5
|
+
* A property line with a filterable ComboBox
|
|
6
|
+
* @param props - ComboBoxProps & PropertyLineProps
|
|
7
|
+
* @returns property-line wrapped ComboBox component
|
|
8
|
+
*/
|
|
9
|
+
export const ComboBoxPropertyLine = (props) => {
|
|
10
|
+
ComboBoxPropertyLine.displayName = "ComboBoxPropertyLine";
|
|
11
|
+
return (_jsx(PropertyLine, { ...props, children: _jsx(ComboBox, { ...props }) }));
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=comboBoxPropertyLine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comboBoxPropertyLine.js","sourceRoot":"","sources":["../../../../../../dev/sharedUiComponents/src/fluent/hoc/propertyLines/comboBoxPropertyLine.tsx"],"names":[],"mappings":";AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAIrD;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAiD,CAAC,KAAK,EAAE,EAAE;IACxF,oBAAoB,CAAC,WAAW,GAAG,sBAAsB,CAAC;IAE1D,OAAO,CACH,KAAC,YAAY,OAAK,KAAK,YACnB,KAAC,QAAQ,OAAK,KAAK,GAAI,GACZ,CAClB,CAAC;AACN,CAAC,CAAC","sourcesContent":["import type { FunctionComponent } from \"react\";\r\nimport type { PropertyLineProps } from \"./propertyLine\";\r\nimport type { ComboBoxProps } from \"../../primitives/comboBox\";\r\n\r\nimport { PropertyLine } from \"./propertyLine\";\r\nimport { ComboBox } from \"../../primitives/comboBox\";\r\n\r\ntype ComboBoxPropertyLineProps = ComboBoxProps & PropertyLineProps<string>;\r\n\r\n/**\r\n * A property line with a filterable ComboBox\r\n * @param props - ComboBoxProps & PropertyLineProps\r\n * @returns property-line wrapped ComboBox component\r\n */\r\nexport const ComboBoxPropertyLine: FunctionComponent<ComboBoxPropertyLineProps> = (props) => {\r\n ComboBoxPropertyLine.displayName = \"ComboBoxPropertyLine\";\r\n\r\n return (\r\n <PropertyLine {...props}>\r\n <ComboBox {...props} />\r\n </PropertyLine>\r\n );\r\n};\r\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { FunctionComponent } from "react";
|
|
2
|
+
import type { BaseTexture } from "@babylonjs/core/Materials/Textures/baseTexture.js";
|
|
3
|
+
import type { Scene } from "@babylonjs/core/scene.js";
|
|
4
|
+
type TextureUploadUpdateProps = {
|
|
5
|
+
/**
|
|
6
|
+
* Existing texture to update via updateURL
|
|
7
|
+
*/
|
|
8
|
+
texture: BaseTexture;
|
|
9
|
+
/**
|
|
10
|
+
* Callback after texture is updated
|
|
11
|
+
*/
|
|
12
|
+
onChange?: (texture: BaseTexture) => void;
|
|
13
|
+
scene?: never;
|
|
14
|
+
cubeOnly?: never;
|
|
15
|
+
};
|
|
16
|
+
type TextureUploadCreateProps = {
|
|
17
|
+
/**
|
|
18
|
+
* The scene to create the texture in
|
|
19
|
+
*/
|
|
20
|
+
scene: Scene;
|
|
21
|
+
/**
|
|
22
|
+
* Callback when a new texture is created
|
|
23
|
+
*/
|
|
24
|
+
onChange: (texture: BaseTexture) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Whether to create cube textures
|
|
27
|
+
*/
|
|
28
|
+
cubeOnly?: boolean;
|
|
29
|
+
texture?: never;
|
|
30
|
+
};
|
|
31
|
+
type TextureUploadProps = TextureUploadUpdateProps | TextureUploadCreateProps;
|
|
32
|
+
/**
|
|
33
|
+
* A button that uploads a file and either:
|
|
34
|
+
* - Updates an existing Texture or CubeTexture via updateURL (if texture prop is provided)
|
|
35
|
+
* - Creates a new Texture or CubeTexture (if scene/onChange props are provided)
|
|
36
|
+
* @param props TextureUploadProps
|
|
37
|
+
* @returns UploadButton component that handles texture upload
|
|
38
|
+
*/
|
|
39
|
+
export declare const TextureUpload: FunctionComponent<TextureUploadProps>;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { Texture } from "@babylonjs/core/Materials/Textures/texture.js";
|
|
4
|
+
import { CubeTexture } from "@babylonjs/core/Materials/Textures/cubeTexture.js";
|
|
5
|
+
import { ReadFile } from "@babylonjs/core/Misc/fileTools.js";
|
|
6
|
+
import { UploadButton } from "../primitives/uploadButton.js";
|
|
7
|
+
/**
|
|
8
|
+
* A button that uploads a file and either:
|
|
9
|
+
* - Updates an existing Texture or CubeTexture via updateURL (if texture prop is provided)
|
|
10
|
+
* - Creates a new Texture or CubeTexture (if scene/onChange props are provided)
|
|
11
|
+
* @param props TextureUploadProps
|
|
12
|
+
* @returns UploadButton component that handles texture upload
|
|
13
|
+
*/
|
|
14
|
+
export const TextureUpload = (props) => {
|
|
15
|
+
TextureUpload.displayName = "TextureUpload";
|
|
16
|
+
const label = props.texture ? "Upload Texture" : undefined;
|
|
17
|
+
// TODO: This should probably be dynamically fetching a list of supported texture extensions
|
|
18
|
+
const accept = ".jpg, .png, .tga, .dds, .env, .exr";
|
|
19
|
+
const handleUpload = useCallback((files) => {
|
|
20
|
+
const file = files[0];
|
|
21
|
+
if (!file) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
ReadFile(file, (data) => {
|
|
25
|
+
const blob = new Blob([data], { type: "octet/stream" });
|
|
26
|
+
// Update existing texture
|
|
27
|
+
if (props.texture) {
|
|
28
|
+
const { texture, onChange } = props;
|
|
29
|
+
const reader = new FileReader();
|
|
30
|
+
reader.readAsDataURL(blob);
|
|
31
|
+
reader.onloadend = () => {
|
|
32
|
+
const base64data = reader.result;
|
|
33
|
+
if (texture instanceof CubeTexture) {
|
|
34
|
+
let extension = undefined;
|
|
35
|
+
if (file.name.toLowerCase().indexOf(".dds") > 0) {
|
|
36
|
+
extension = ".dds";
|
|
37
|
+
}
|
|
38
|
+
else if (file.name.toLowerCase().indexOf(".env") > 0) {
|
|
39
|
+
extension = ".env";
|
|
40
|
+
}
|
|
41
|
+
texture.updateURL(base64data, extension, () => onChange?.(texture));
|
|
42
|
+
}
|
|
43
|
+
else if (texture instanceof Texture) {
|
|
44
|
+
texture.updateURL(base64data, null, () => onChange?.(texture));
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Create new texture
|
|
50
|
+
const { scene, cubeOnly, onChange } = props;
|
|
51
|
+
const url = URL.createObjectURL(blob);
|
|
52
|
+
const extension = file.name.split(".").pop()?.toLowerCase();
|
|
53
|
+
// Revoke the object URL after texture loads to prevent memory leak
|
|
54
|
+
const revokeUrl = () => URL.revokeObjectURL(url);
|
|
55
|
+
const newTexture = cubeOnly
|
|
56
|
+
? new CubeTexture(url, scene, [], false, undefined, revokeUrl, undefined, undefined, false, extension ? "." + extension : undefined)
|
|
57
|
+
: new Texture(url, scene, false, false, undefined, revokeUrl);
|
|
58
|
+
onChange(newTexture);
|
|
59
|
+
}
|
|
60
|
+
}, undefined, true);
|
|
61
|
+
}, [props]);
|
|
62
|
+
return _jsx(UploadButton, { onUpload: handleUpload, accept: accept, title: "Upload Texture", label: label });
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=textureUpload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textureUpload.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/hoc/textureUpload.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { FunctionComponent } from \"react\";\r\nimport { useCallback } from \"react\";\r\nimport type { BaseTexture } from \"core/Materials/Textures/baseTexture\";\r\nimport type { Scene } from \"core/scene\";\r\nimport { Texture } from \"core/Materials/Textures/texture\";\r\nimport { CubeTexture } from \"core/Materials/Textures/cubeTexture\";\r\nimport { ReadFile } from \"core/Misc/fileTools\";\r\nimport { UploadButton } from \"../primitives/uploadButton\";\r\n\r\ntype TextureUploadUpdateProps = {\r\n /**\r\n * Existing texture to update via updateURL\r\n */\r\n texture: BaseTexture;\r\n /**\r\n * Callback after texture is updated\r\n */\r\n onChange?: (texture: BaseTexture) => void;\r\n scene?: never;\r\n cubeOnly?: never;\r\n};\r\n\r\ntype TextureUploadCreateProps = {\r\n /**\r\n * The scene to create the texture in\r\n */\r\n scene: Scene;\r\n /**\r\n * Callback when a new texture is created\r\n */\r\n onChange: (texture: BaseTexture) => void;\r\n /**\r\n * Whether to create cube textures\r\n */\r\n cubeOnly?: boolean;\r\n texture?: never;\r\n};\r\n\r\ntype TextureUploadProps = TextureUploadUpdateProps | TextureUploadCreateProps;\r\n\r\n/**\r\n * A button that uploads a file and either:\r\n * - Updates an existing Texture or CubeTexture via updateURL (if texture prop is provided)\r\n * - Creates a new Texture or CubeTexture (if scene/onChange props are provided)\r\n * @param props TextureUploadProps\r\n * @returns UploadButton component that handles texture upload\r\n */\r\nexport const TextureUpload: FunctionComponent<TextureUploadProps> = (props) => {\r\n TextureUpload.displayName = \"TextureUpload\";\r\n const label = props.texture ? \"Upload Texture\" : undefined;\r\n // TODO: This should probably be dynamically fetching a list of supported texture extensions\r\n const accept = \".jpg, .png, .tga, .dds, .env, .exr\";\r\n const handleUpload = useCallback(\r\n (files: FileList) => {\r\n const file = files[0];\r\n if (!file) {\r\n return;\r\n }\r\n\r\n ReadFile(\r\n file,\r\n (data) => {\r\n const blob = new Blob([data], { type: \"octet/stream\" });\r\n\r\n // Update existing texture\r\n if (props.texture) {\r\n const { texture, onChange } = props;\r\n const reader = new FileReader();\r\n reader.readAsDataURL(blob);\r\n reader.onloadend = () => {\r\n const base64data = reader.result as string;\r\n\r\n if (texture instanceof CubeTexture) {\r\n let extension: string | undefined = undefined;\r\n if (file.name.toLowerCase().indexOf(\".dds\") > 0) {\r\n extension = \".dds\";\r\n } else if (file.name.toLowerCase().indexOf(\".env\") > 0) {\r\n extension = \".env\";\r\n }\r\n texture.updateURL(base64data, extension, () => onChange?.(texture));\r\n } else if (texture instanceof Texture) {\r\n texture.updateURL(base64data, null, () => onChange?.(texture));\r\n }\r\n };\r\n } else {\r\n // Create new texture\r\n const { scene, cubeOnly, onChange } = props;\r\n const url = URL.createObjectURL(blob);\r\n const extension = file.name.split(\".\").pop()?.toLowerCase();\r\n\r\n // Revoke the object URL after texture loads to prevent memory leak\r\n const revokeUrl = () => URL.revokeObjectURL(url);\r\n\r\n const newTexture = cubeOnly\r\n ? new CubeTexture(url, scene, [], false, undefined, revokeUrl, undefined, undefined, false, extension ? \".\" + extension : undefined)\r\n : new Texture(url, scene, false, false, undefined, revokeUrl);\r\n\r\n onChange(newTexture);\r\n }\r\n },\r\n undefined,\r\n true\r\n );\r\n },\r\n [props]\r\n );\r\n\r\n return <UploadButton onUpload={handleUpload} accept={accept} title={\"Upload Texture\"} label={label} />;\r\n};\r\n"]}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import type { FunctionComponent } from "react";
|
|
2
1
|
import type { FluentIcon } from "@fluentui/react-icons";
|
|
3
2
|
import type { BasePrimitiveProps } from "./primitive.js";
|
|
4
3
|
export type ButtonProps = BasePrimitiveProps & {
|
|
5
|
-
onClick
|
|
4
|
+
onClick?: () => void;
|
|
6
5
|
icon?: FluentIcon;
|
|
7
6
|
appearance?: "subtle" | "transparent" | "primary";
|
|
8
7
|
label?: string;
|
|
9
8
|
};
|
|
10
|
-
export declare const Button:
|
|
9
|
+
export declare const Button: import("react").ForwardRefExoticComponent<BasePrimitiveProps & {
|
|
10
|
+
onClick?: () => void;
|
|
11
|
+
icon?: FluentIcon;
|
|
12
|
+
appearance?: "subtle" | "transparent" | "primary";
|
|
13
|
+
label?: string;
|
|
14
|
+
} & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Button as FluentButton } from "@fluentui/react-components";
|
|
3
|
-
import { useContext } from "react";
|
|
3
|
+
import { forwardRef, useContext } from "react";
|
|
4
4
|
import { ToolContext } from "../hoc/fluentToolWrapper.js";
|
|
5
|
-
export const Button = (props) => {
|
|
6
|
-
Button.displayName = "Button";
|
|
5
|
+
export const Button = forwardRef((props, ref) => {
|
|
7
6
|
const { size } = useContext(ToolContext);
|
|
8
7
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
9
8
|
const { icon: Icon, label, ...buttonProps } = props;
|
|
10
|
-
return (_jsx(FluentButton, { iconPosition: "after", ...buttonProps, size: size, icon: Icon && _jsx(Icon, {}), children: label && props.label }));
|
|
11
|
-
};
|
|
9
|
+
return (_jsx(FluentButton, { ref: ref, iconPosition: "after", ...buttonProps, size: size, icon: Icon && _jsx(Icon, {}), children: label && props.label }));
|
|
10
|
+
});
|
|
11
|
+
Button.displayName = "Button";
|
|
12
12
|
//# sourceMappingURL=button.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/button.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"button.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/button.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAG/C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AASvD,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAAiC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IAC5E,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACzC,gEAAgE;IAChE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,WAAW,EAAE,GAAG,KAAK,CAAC;IACpD,OAAO,CACH,KAAC,YAAY,IAAC,GAAG,EAAE,GAAG,EAAE,YAAY,EAAC,OAAO,KAAK,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,KAAC,IAAI,KAAG,YAC3F,KAAK,IAAI,KAAK,CAAC,KAAK,GACV,CAClB,CAAC;AACN,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC","sourcesContent":["import { Button as FluentButton } from \"@fluentui/react-components\";\r\nimport { forwardRef, useContext } from \"react\";\r\nimport type { FluentIcon } from \"@fluentui/react-icons\";\r\nimport type { BasePrimitiveProps } from \"./primitive\";\r\nimport { ToolContext } from \"../hoc/fluentToolWrapper\";\r\n\r\nexport type ButtonProps = BasePrimitiveProps & {\r\n onClick?: () => void;\r\n icon?: FluentIcon;\r\n appearance?: \"subtle\" | \"transparent\" | \"primary\";\r\n label?: string;\r\n};\r\n\r\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {\r\n const { size } = useContext(ToolContext);\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n const { icon: Icon, label, ...buttonProps } = props;\r\n return (\r\n <FluentButton ref={ref} iconPosition=\"after\" {...buttonProps} size={size} icon={Icon && <Icon />}>\r\n {label && props.label}\r\n </FluentButton>\r\n );\r\n});\r\n\r\nButton.displayName = \"Button\";\r\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { BaseTexture } from "@babylonjs/core/Materials/Textures/baseTexture.js";
|
|
2
|
+
import type { Scene } from "@babylonjs/core/scene.js";
|
|
3
|
+
import type { Nullable } from "@babylonjs/core/types.js";
|
|
4
|
+
import type { FunctionComponent } from "react";
|
|
5
|
+
import type { PrimitiveProps } from "./primitive.js";
|
|
6
|
+
export type ChooseTextureProps = PrimitiveProps<Nullable<BaseTexture>> & {
|
|
7
|
+
/**
|
|
8
|
+
* The scene to get textures from
|
|
9
|
+
*/
|
|
10
|
+
scene: Scene;
|
|
11
|
+
/**
|
|
12
|
+
* File types to accept for upload
|
|
13
|
+
*/
|
|
14
|
+
accept?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Whether to only allow cube textures
|
|
17
|
+
*/
|
|
18
|
+
cubeOnly?: boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* A primitive component with a ComboBox for selecting from existing scene textures
|
|
22
|
+
* and a button for uploading new texture files.
|
|
23
|
+
* @param props ChooseTextureProps
|
|
24
|
+
* @returns ChooseTexture component
|
|
25
|
+
*/
|
|
26
|
+
export declare const ChooseTexture: FunctionComponent<ChooseTextureProps>;
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
/* eslint-disable jsdoc/require-returns */
|
|
3
3
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
4
4
|
import { useState, useEffect, useCallback, useContext } from "react";
|
|
5
|
-
import { ColorPicker as FluentColorPicker, ColorSlider, ColorArea, AlphaSlider, makeStyles,
|
|
5
|
+
import { ColorPicker as FluentColorPicker, ColorSlider, ColorArea, AlphaSlider, makeStyles, tokens, Body1Strong, ColorSwatch, Body1 } from "@fluentui/react-components";
|
|
6
6
|
import { Color3, Color4 } from "@babylonjs/core/Maths/math.color.js";
|
|
7
7
|
import { SpinButton } from "./spinButton.js";
|
|
8
8
|
import { TextInput } from "./textInput.js";
|
|
@@ -10,6 +10,7 @@ import { NumberDropdown } from "./dropdown.js";
|
|
|
10
10
|
import { ValidateColorHex } from "./utils.js";
|
|
11
11
|
import { Link } from "./link.js";
|
|
12
12
|
import { ToolContext } from "../hoc/fluentToolWrapper.js";
|
|
13
|
+
import { Popover } from "./popover.js";
|
|
13
14
|
const useColorPickerStyles = makeStyles({
|
|
14
15
|
container: {
|
|
15
16
|
width: "350px",
|
|
@@ -61,7 +62,6 @@ export const ColorPickerPopup = (props) => {
|
|
|
61
62
|
ColorPickerPopup.displayName = "ColorPickerPopup";
|
|
62
63
|
const classes = useColorPickerStyles();
|
|
63
64
|
const [color, setColor] = useState(props.value);
|
|
64
|
-
const [popoverOpen, setPopoverOpen] = useState(false);
|
|
65
65
|
const [isLinear, setIsLinear] = useState(props.isLinearMode ?? false);
|
|
66
66
|
const [isFloat, setFloat] = useState(false);
|
|
67
67
|
const { size } = useContext(ToolContext);
|
|
@@ -79,23 +79,19 @@ export const ColorPickerPopup = (props) => {
|
|
|
79
79
|
setColor(newColor);
|
|
80
80
|
props.onChange(newColor); // Ensures the parent is notified when color changes from within colorPicker
|
|
81
81
|
};
|
|
82
|
-
return (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}, options: [
|
|
96
|
-
{ label: "Int", value: 0 },
|
|
97
|
-
{ label: "Float", value: 1 },
|
|
98
|
-
], 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 }) })] }) })] }));
|
|
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
|
+
label: "Color Space",
|
|
84
|
+
info: _jsx(Body1, { children: "Today this is not mutable as the color space is determined by the entity. Soon we will allow swapping" }),
|
|
85
|
+
}, options: [
|
|
86
|
+
{ label: "Gamma", value: 0 },
|
|
87
|
+
{ label: "Linear", value: 1 },
|
|
88
|
+
], disabled: true, value: isLinear ? 1 : 0, onChange: (val) => setIsLinear(val === 1) }), _jsx(NumberDropdown, { className: classes.inputField, infoLabel: {
|
|
89
|
+
label: "Data Type",
|
|
90
|
+
info: _jsx(Body1, { children: "We will introduce this functionality soon!" }),
|
|
91
|
+
}, options: [
|
|
92
|
+
{ label: "Int", value: 0 },
|
|
93
|
+
{ label: "Float", value: 1 },
|
|
94
|
+
], 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 }) })] }) }));
|
|
99
95
|
};
|
|
100
96
|
/**
|
|
101
97
|
* Component which displays the passed in color's HEX value, either in linearSpace (if linearHex is true) or in gamma space
|
|
@@ -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 {\r\n ColorPicker as FluentColorPicker,\r\n ColorSlider,\r\n ColorArea,\r\n AlphaSlider,\r\n makeStyles,\r\n Popover,\r\n PopoverSurface,\r\n PopoverTrigger,\r\n tokens,\r\n Body1Strong,\r\n ColorSwatch,\r\n Body1,\r\n} 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\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 [popoverOpen, setPopoverOpen] = useState(false);\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 positioning={{\r\n align: \"start\",\r\n overflowBoundary: document.body,\r\n autoSize: true,\r\n }}\r\n open={popoverOpen}\r\n trapFocus\r\n onOpenChange={(_, data) => setPopoverOpen(data.open)}\r\n >\r\n <PopoverTrigger disableButtonEnhancement>\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 </PopoverTrigger>\r\n\r\n <PopoverSurface>\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 </PopoverSurface>\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 { 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,8 +1,11 @@
|
|
|
1
1
|
import type { FunctionComponent } from "react";
|
|
2
|
-
|
|
2
|
+
import type { PrimitiveProps } from "./primitive.js";
|
|
3
|
+
export type ComboBoxProps = PrimitiveProps<string> & {
|
|
3
4
|
label: string;
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The list of options to display
|
|
7
|
+
*/
|
|
8
|
+
options: string[];
|
|
6
9
|
};
|
|
7
10
|
/**
|
|
8
11
|
* Wrapper around a Fluent ComboBox that allows for filtering options
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useState, useContext, useEffect } from "react";
|
|
3
3
|
import { Combobox as FluentComboBox, makeStyles, useComboboxFilter, useId } from "@fluentui/react-components";
|
|
4
|
+
import { ToolContext } from "../hoc/fluentToolWrapper.js";
|
|
5
|
+
import { CustomTokens } from "./utils.js";
|
|
4
6
|
const useStyles = makeStyles({
|
|
5
7
|
root: {
|
|
6
8
|
// Stack the label above the field with a gap
|
|
@@ -10,6 +12,14 @@ const useStyles = makeStyles({
|
|
|
10
12
|
gap: "2px",
|
|
11
13
|
maxWidth: "400px",
|
|
12
14
|
},
|
|
15
|
+
comboBox: {
|
|
16
|
+
width: CustomTokens.inputWidth,
|
|
17
|
+
minWidth: CustomTokens.inputWidth,
|
|
18
|
+
boxSizing: "border-box",
|
|
19
|
+
},
|
|
20
|
+
input: {
|
|
21
|
+
minWidth: 0, // Override Fluent's default minWidth on the input
|
|
22
|
+
},
|
|
13
23
|
});
|
|
14
24
|
/**
|
|
15
25
|
* Wrapper around a Fluent ComboBox that allows for filtering options
|
|
@@ -20,14 +30,19 @@ export const ComboBox = (props) => {
|
|
|
20
30
|
ComboBox.displayName = "ComboBox";
|
|
21
31
|
const comboId = useId();
|
|
22
32
|
const styles = useStyles();
|
|
23
|
-
const
|
|
24
|
-
const
|
|
33
|
+
const { size } = useContext(ToolContext);
|
|
34
|
+
const [query, setQuery] = useState(props.value ?? "");
|
|
35
|
+
// Sync query with props.value when it changes externally
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
setQuery(props.value ?? "");
|
|
38
|
+
}, [props.value]);
|
|
39
|
+
const children = useComboboxFilter(query, props.options, {
|
|
25
40
|
noOptionsMessage: "No items match your search.",
|
|
26
41
|
});
|
|
27
42
|
const onOptionSelect = (_e, data) => {
|
|
28
43
|
setQuery(data.optionText ?? "");
|
|
29
44
|
data.optionText && props.onChange(data.optionText);
|
|
30
45
|
};
|
|
31
|
-
return (_jsxs("div", { className: styles.root, children: [_jsx("label", { id: comboId, children: props.label }), _jsx(FluentComboBox, { onOptionSelect: onOptionSelect, "aria-labelledby": comboId, placeholder: "Search..", onChange: (ev) => setQuery(ev.target.value), value: query, children: children })] }));
|
|
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, onBlur: () => props.onChange(query), "aria-labelledby": comboId, placeholder: "Search..", onChange: (ev) => setQuery(ev.target.value), value: query, children: children })] }));
|
|
32
47
|
};
|
|
33
48
|
//# 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,MAAM,OAAO,CAAC;
|
|
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;AAE9G,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,MAAM,SAAS,GAAG,UAAU,CAAC;IACzB,IAAI,EAAE;QACF,6CAA6C;QAC7C,OAAO,EAAE,MAAM;QACf,gBAAgB,EAAE,aAAa;QAC/B,YAAY,EAAE,OAAO;QACrB,GAAG,EAAE,KAAK;QACV,QAAQ,EAAE,OAAO;KACpB;IACD,QAAQ,EAAE;QACN,KAAK,EAAE,YAAY,CAAC,UAAU;QAC9B,QAAQ,EAAE,YAAY,CAAC,UAAU;QACjC,SAAS,EAAE,YAAY;KAC1B;IACD,KAAK,EAAE;QACH,QAAQ,EAAE,CAAC,EAAE,kDAAkD;KAClE;CACJ,CAAC,CAAC;AASH;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAqC,CAAC,KAAK,EAAE,EAAE;IAChE,QAAQ,CAAC,WAAW,GAAG,UAAU,CAAC;IAClC,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;IACxB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IAEzC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IAEtD,yDAAyD;IACzD,SAAS,CAAC,GAAG,EAAE;QACX,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAElB,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE;QACrD,gBAAgB,EAAE,6BAA6B;KAClD,CAAC,CAAC;IACH,MAAM,cAAc,GAAG,CAAC,EAAmB,EAAE,IAAwB,EAAE,EAAE;QACrE,QAAQ,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvD,CAAC,CAAC;IAEF,OAAO,CACH,eAAK,SAAS,EAAE,MAAM,CAAC,IAAI,aACvB,gBAAO,EAAE,EAAE,OAAO,YAAG,KAAK,CAAC,KAAK,GAAS,EACzC,KAAC,cAAc,IACX,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,EACpC,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,EAAE,EAClC,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,qBAClB,OAAO,EACxB,WAAW,EAAC,UAAU,EACtB,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAC3C,KAAK,EAAE,KAAK,YAEX,QAAQ,GACI,IACf,CACT,CAAC;AACN,CAAC,CAAC","sourcesContent":["import type { FunctionComponent } from \"react\";\r\nimport { useState, useContext, useEffect } from \"react\";\r\nimport { Combobox as FluentComboBox, makeStyles, useComboboxFilter, useId } 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 // Stack the label above the field with a gap\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, // Override Fluent's default minWidth on the input\r\n },\r\n});\r\n\r\nexport type ComboBoxProps = PrimitiveProps<string> & {\r\n label: string;\r\n /**\r\n * The list of options to display\r\n */\r\n options: string[];\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 const [query, setQuery] = useState(props.value ?? \"\");\r\n\r\n // Sync query with props.value when it changes externally\r\n useEffect(() => {\r\n setQuery(props.value ?? \"\");\r\n }, [props.value]);\r\n\r\n const children = useComboboxFilter(query, props.options, {\r\n noOptionsMessage: \"No items match your search.\",\r\n });\r\n const onOptionSelect = (_e: SelectionEvents, data: OptionOnSelectData) => {\r\n setQuery(data.optionText ?? \"\");\r\n data.optionText && props.onChange(data.optionText);\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 onBlur={() => props.onChange(query)}\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,13 @@
|
|
|
1
|
+
import type { FunctionComponent, PropsWithChildren, ReactElement } from "react";
|
|
2
|
+
import type { FluentIcon } from "@fluentui/react-icons";
|
|
3
|
+
type PopoverWithIconProps = {
|
|
4
|
+
icon: FluentIcon;
|
|
5
|
+
trigger?: never;
|
|
6
|
+
};
|
|
7
|
+
type PopoverWithTriggerProps = {
|
|
8
|
+
icon?: never;
|
|
9
|
+
trigger: ReactElement;
|
|
10
|
+
};
|
|
11
|
+
type PopoverProps = PopoverWithIconProps | PopoverWithTriggerProps;
|
|
12
|
+
export declare const Popover: FunctionComponent<PropsWithChildren<PopoverProps>>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Popover as FluentPopover, PopoverTrigger, PopoverSurface, makeStyles, tokens } from "@fluentui/react-components";
|
|
4
|
+
import { Button } from "shared-ui-components/fluent/primitives/button";
|
|
5
|
+
const useStyles = makeStyles({
|
|
6
|
+
surface: {
|
|
7
|
+
maxWidth: "400px",
|
|
8
|
+
},
|
|
9
|
+
content: {
|
|
10
|
+
display: "flex",
|
|
11
|
+
flexDirection: "column",
|
|
12
|
+
gap: tokens.spacingVerticalM,
|
|
13
|
+
padding: tokens.spacingHorizontalL,
|
|
14
|
+
minWidth: "300px",
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
export const Popover = (props) => {
|
|
18
|
+
const { children } = props;
|
|
19
|
+
const [popoverOpen, setPopoverOpen] = useState(false);
|
|
20
|
+
const classes = useStyles();
|
|
21
|
+
return (_jsxs(FluentPopover, { open: popoverOpen, onOpenChange: (_, data) => setPopoverOpen(data.open), positioning: {
|
|
22
|
+
align: "start",
|
|
23
|
+
overflowBoundary: document.body,
|
|
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
|
+
};
|
|
27
|
+
//# sourceMappingURL=popover.js.map
|
|
@@ -0,0 +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;AACjC,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,GAAuD,CAAC,KAAK,EAAE,EAAE;IACjF,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,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,GAAI,GAAkB,EAC9I,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","sourcesContent":["import type { FunctionComponent, PropsWithChildren, ReactElement } from \"react\";\r\nimport { 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: FunctionComponent<PropsWithChildren<PopoverProps>> = (props) => {\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 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"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FunctionComponent } from "react";
|
|
2
|
+
import type { ButtonProps } from "./button.js";
|
|
3
|
+
type UploadButtonProps = Omit<ButtonProps, "onClick" | "icon"> & {
|
|
4
|
+
/**
|
|
5
|
+
* Callback when files are selected
|
|
6
|
+
*/
|
|
7
|
+
onUpload: (files: FileList) => void;
|
|
8
|
+
/**
|
|
9
|
+
* File types to accept (e.g., ".jpg, .png, .dds")
|
|
10
|
+
*/
|
|
11
|
+
accept?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Text label to display on the button (optional)
|
|
14
|
+
*/
|
|
15
|
+
label?: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* A button that triggers a file upload dialog.
|
|
19
|
+
* Combines a Button with a hidden file input.
|
|
20
|
+
* @param props UploadButtonProps
|
|
21
|
+
* @returns UploadButton component
|
|
22
|
+
*/
|
|
23
|
+
export declare const UploadButton: FunctionComponent<UploadButtonProps>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef } from "react";
|
|
3
|
+
import { ArrowUploadRegular } from "@fluentui/react-icons";
|
|
4
|
+
import { Button } from "./button.js";
|
|
5
|
+
/**
|
|
6
|
+
* A button that triggers a file upload dialog.
|
|
7
|
+
* Combines a Button with a hidden file input.
|
|
8
|
+
* @param props UploadButtonProps
|
|
9
|
+
* @returns UploadButton component
|
|
10
|
+
*/
|
|
11
|
+
export const UploadButton = (props) => {
|
|
12
|
+
const { onUpload, accept, label, ...buttonProps } = props;
|
|
13
|
+
UploadButton.displayName = "UploadButton";
|
|
14
|
+
const inputRef = useRef(null);
|
|
15
|
+
const handleClick = () => {
|
|
16
|
+
inputRef.current?.click();
|
|
17
|
+
};
|
|
18
|
+
const handleChange = (evt) => {
|
|
19
|
+
const files = evt.target.files;
|
|
20
|
+
if (files && files.length) {
|
|
21
|
+
onUpload(files);
|
|
22
|
+
}
|
|
23
|
+
evt.target.value = "";
|
|
24
|
+
};
|
|
25
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, { icon: ArrowUploadRegular, title: label ?? "Upload", label: label, onClick: handleClick, ...buttonProps }), _jsx("input", { ref: inputRef, type: "file", accept: accept, style: { display: "none" }, onChange: handleChange })] }));
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=uploadButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploadButton.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/uploadButton.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAkBlC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAyC,CAAC,KAAK,EAAE,EAAE;IACxE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,WAAW,EAAE,GAAG,KAAK,CAAC;IAC1D,YAAY,CAAC,WAAW,GAAG,cAAc,CAAC;IAC1C,MAAM,QAAQ,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;IAEhD,MAAM,WAAW,GAAG,GAAG,EAAE;QACrB,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CAAC,GAAwC,EAAE,EAAE;QAC9D,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;QAC/B,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACxB,QAAQ,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;QACD,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;IAC1B,CAAC,CAAC;IAEF,OAAO,CACH,8BACI,KAAC,MAAM,IAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,KAAK,IAAI,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,KAAM,WAAW,GAAI,EACnH,gBAAO,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAC,MAAM,EAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,YAAY,GAAI,IACzG,CACN,CAAC;AACN,CAAC,CAAC","sourcesContent":["import type { FunctionComponent } from \"react\";\r\nimport { useRef } from \"react\";\r\nimport { ArrowUploadRegular } from \"@fluentui/react-icons\";\r\nimport { Button } from \"./button\";\r\nimport type { ButtonProps } from \"./button\";\r\n\r\ntype UploadButtonProps = Omit<ButtonProps, \"onClick\" | \"icon\"> & {\r\n /**\r\n * Callback when files are selected\r\n */\r\n onUpload: (files: FileList) => void;\r\n /**\r\n * File types to accept (e.g., \".jpg, .png, .dds\")\r\n */\r\n accept?: string;\r\n /**\r\n * Text label to display on the button (optional)\r\n */\r\n label?: string;\r\n};\r\n\r\n/**\r\n * A button that triggers a file upload dialog.\r\n * Combines a Button with a hidden file input.\r\n * @param props UploadButtonProps\r\n * @returns UploadButton component\r\n */\r\nexport const UploadButton: FunctionComponent<UploadButtonProps> = (props) => {\r\n const { onUpload, accept, label, ...buttonProps } = props;\r\n UploadButton.displayName = \"UploadButton\";\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n\r\n const handleClick = () => {\r\n inputRef.current?.click();\r\n };\r\n\r\n const handleChange = (evt: React.ChangeEvent<HTMLInputElement>) => {\r\n const files = evt.target.files;\r\n if (files && files.length) {\r\n onUpload(files);\r\n }\r\n evt.target.value = \"\";\r\n };\r\n\r\n return (\r\n <>\r\n <Button icon={ArrowUploadRegular} title={label ?? \"Upload\"} label={label} onClick={handleClick} {...buttonProps} />\r\n <input ref={inputRef} type=\"file\" accept={accept} style={{ display: \"none\" }} onChange={handleChange} />\r\n </>\r\n );\r\n};\r\n"]}
|