@onerjs/shared-ui-components 8.42.8 → 8.43.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,14 +13,12 @@ export declare const ColorPickerPopup: import("react").ForwardRefExoticComponent
13
13
  onChange: (value: Color3 | Color4) => void;
14
14
  } & import("react").RefAttributes<HTMLButtonElement>>;
15
15
  export type InputHexProps = PrimitiveProps<Color3 | Color4> & {
16
- linearHex?: boolean;
17
- isLinearMode?: boolean;
16
+ isLinear?: boolean;
17
+ ispropertyLinear?: boolean;
18
18
  };
19
19
  /**
20
- * Component which displays the passed in color's HEX value, either in linearSpace (if linearHex is true) or in gamma space
21
- * When the hex color is changed by user, component calculates the new Color3/4 value and calls onChange
22
- *
23
- * Component uses the isLinearMode boolean to display an informative label regarding linear / gamma space
20
+ * Component which displays the passed in color's HEX value in the currently selected color space.
21
+ * When the hex color is changed by user, component calculates the new Color3/4 value and calls onChange.
24
22
  * @param props - The properties for the InputHexField component.
25
23
  * @returns
26
24
  */
@@ -28,8 +26,7 @@ export declare const InputHexField: FunctionComponent<InputHexProps>;
28
26
  type HsvKey = "h" | "s" | "v";
29
27
  type InputHsvFieldProps = PrimitiveProps<Color3 | Color4> & {
30
28
  hsvKey: HsvKey;
31
- max: number;
32
- scale?: number;
29
+ isFloat: boolean;
33
30
  };
34
31
  /**
35
32
  * 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.
@@ -1,7 +1,7 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /* eslint-disable jsdoc/require-returns */
3
3
  /* eslint-disable @typescript-eslint/naming-convention */
4
- import { forwardRef, useState, useEffect, useCallback, useContext } from "react";
4
+ import { forwardRef, useState, useEffect, useCallback, useContext, useMemo } 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";
@@ -73,68 +73,118 @@ export const ColorPickerPopup = forwardRef((props, ref) => {
73
73
  useEffect(() => {
74
74
  setColor(value); // Ensures the trigger color updates when props.value changes
75
75
  }, [value]);
76
+ const isPropertyLinear = isLinearMode ?? false;
77
+ /** Color in gamma space — used for visual elements (picker, preview, trigger) */
78
+ const gammaColor = useMemo(() => (isPropertyLinear ? color.toGammaSpace() : color), [color, isPropertyLinear]);
79
+ /** Color in the user-selected display space — used for numeric inputs (RGB, HSV, Hex) */
80
+ const displayColor = useMemo(() => (isLinear === isPropertyLinear ? color : isLinear ? color.toLinearSpace() : color.toGammaSpace()), [color, isLinear, isPropertyLinear]);
76
81
  const handleColorPickerChange = (_, data) => {
77
- let color = Color3.FromHSV(data.color.h, data.color.s, data.color.v);
82
+ // The visual picker always operates in gamma space, convert back to property space
83
+ let gammaResult = Color3.FromHSV(data.color.h, data.color.s, data.color.v);
78
84
  if (value instanceof Color4) {
79
- color = Color4.FromColor3(color, data.color.a ?? 1);
85
+ gammaResult = Color4.FromColor3(gammaResult, data.color.a ?? 1);
80
86
  }
81
- handleChange(color);
87
+ handleChange(isPropertyLinear ? gammaResult.toLinearSpace() : gammaResult);
82
88
  };
83
89
  const handleChange = (newColor) => {
84
90
  setColor(newColor);
85
91
  onChange(newColor); // Ensures the parent is notified when color changes from within colorPicker
86
92
  };
87
- return (_jsx(Popover, { trigger: _jsx(ColorSwatch, { className: classes.trigger, 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: {
93
+ const handleDisplayChange = (newDisplayColor) => {
94
+ const propertyColor = isLinear === isPropertyLinear ? newDisplayColor : isLinear ? newDisplayColor.toGammaSpace() : newDisplayColor.toLinearSpace();
95
+ handleChange(propertyColor);
96
+ };
97
+ return (_jsx(Popover, { trigger: _jsx(ColorSwatch, { className: classes.trigger, ref: ref, ...rest, borderColor: tokens.colorNeutralShadowKeyDarker, size: size === "small" ? "extra-small" : "small", shape: "rounded", color: gammaColor.toHexString(), value: gammaColor.toHexString().slice(1) }), children: _jsxs("div", { className: classes.container, children: [_jsxs(FluentColorPicker, { className: classes.colorPicker, color: rgbaToHsv(gammaColor), 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: gammaColor.toHexString() } }), _jsx(NumberDropdown, { className: classes.inputField, infoLabel: {
88
98
  label: "Color Space",
89
- info: _jsx(Body1, { children: "Today this is not mutable as the color space is determined by the entity. Soon we will allow swapping" }),
99
+ info: (_jsxs(Body1, { children: ["Choose which color space to display numeric values in. This property stores its color in", " ", _jsx(Body1Strong, { children: isPropertyLinear ? "linear" : "gamma" }), " space. The visual picker always shows gamma (screen-accurate) colors."] })),
90
100
  }, options: [
91
101
  { label: "Gamma", value: 0 },
92
102
  { label: "Linear", value: 1 },
93
- ], disabled: true, value: isLinear ? 1 : 0, onChange: (val) => setIsLinear(val === 1) }), _jsx(NumberDropdown, { className: classes.inputField, infoLabel: {
103
+ ], value: isLinear ? 1 : 0, onChange: (val) => setIsLinear(val === 1) }), _jsx(NumberDropdown, { className: classes.inputField, infoLabel: {
94
104
  label: "Data Type",
95
- info: _jsx(Body1, { children: "We will introduce this functionality soon!" }),
105
+ info: (_jsxs(Body1, { children: [_jsx(Body1Strong, { children: "Int" }), " displays RGB channels as integers in the 0\u2013255 range. ", _jsx(Body1Strong, { children: "Float" }), " displays them as decimals in the 0\u20131 range. This is display-only and does not affect the stored color."] })),
96
106
  }, options: [
97
107
  { label: "Int", value: 0 },
98
108
  { label: "Float", value: 1 },
99
- ], 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 }) })] }) }));
109
+ ], value: isFloat ? 1 : 0, onChange: (val) => setFloat(val === 1) })] }), _jsxs("div", { className: classes.inputRow, children: [_jsx(InputRgbField, { title: "Red", value: displayColor, rgbKey: "r", isFloat: isFloat, onChange: handleDisplayChange }), _jsx(InputRgbField, { title: "Green", value: displayColor, rgbKey: "g", isFloat: isFloat, onChange: handleDisplayChange }), _jsx(InputRgbField, { title: "Blue", value: displayColor, rgbKey: "b", isFloat: isFloat, onChange: handleDisplayChange }), _jsx(InputAlphaField, { color: color, onChange: handleChange })] }), _jsxs("div", { className: classes.inputRow, children: [_jsx(InputHsvField, { title: "Hue", value: displayColor, hsvKey: "h", isFloat: isFloat, onChange: handleDisplayChange }), _jsx(InputHsvField, { title: "Saturation", value: displayColor, hsvKey: "s", isFloat: isFloat, onChange: handleDisplayChange }), _jsx(InputHsvField, { title: "Value", value: displayColor, hsvKey: "v", isFloat: isFloat, onChange: handleDisplayChange })] }), _jsx("div", { className: classes.inputRow, children: _jsx(InputHexField, { title: "Hexadecimal", value: displayColor, isLinear: isLinear, ispropertyLinear: isPropertyLinear, onChange: handleDisplayChange }) })] }) }));
100
110
  });
101
111
  /**
102
- * Component which displays the passed in color's HEX value, either in linearSpace (if linearHex is true) or in gamma space
103
- * When the hex color is changed by user, component calculates the new Color3/4 value and calls onChange
104
- *
105
- * Component uses the isLinearMode boolean to display an informative label regarding linear / gamma space
112
+ * Converts a hex string to the same Color type as the original.
113
+ * Supports "#RGB", "#RRGGBB", and "#RRGGBBAA" formats.
114
+ * For Color4, honors alpha from "#RRGGBBAA" input or preserves the original alpha otherwise.
115
+ * @param hex - The hex string to convert, in one of the supported formats.
116
+ * @param original - The original color, used to determine whether to return a Color3 or Color4 and to preserve alpha if not specified in hex.
117
+ * @returns A new Color3 or Color4 instance representing the hex color
118
+ */
119
+ function colorFromHex(hex, original) {
120
+ const digits = hex.startsWith("#") ? hex.slice(1) : hex;
121
+ // Normalize short hex (RGB => RRGGBB)
122
+ if (digits.length === 3) {
123
+ hex = `#${digits[0]}${digits[0]}${digits[1]}${digits[1]}${digits[2]}${digits[2]}`;
124
+ }
125
+ // 8 hex digits = RRGGBBAA — use Color4.FromHexString which natively handles this
126
+ if (digits.length === 8) {
127
+ if (original instanceof Color4) {
128
+ return Color4.FromHexString(hex);
129
+ }
130
+ return Color3.FromHexString(hex.slice(0, 7));
131
+ }
132
+ // 6 hex digits = RRGGBB (or normalized from 3)
133
+ if (original instanceof Color4) {
134
+ return Color4.FromColor3(Color3.FromHexString(hex), original.a);
135
+ }
136
+ return Color3.FromHexString(hex);
137
+ }
138
+ /**
139
+ * Component which displays the passed in color's HEX value in the currently selected color space.
140
+ * When the hex color is changed by user, component calculates the new Color3/4 value and calls onChange.
106
141
  * @param props - The properties for the InputHexField component.
107
142
  * @returns
108
143
  */
109
144
  export const InputHexField = (props) => {
110
145
  const classes = useColorPickerStyles();
111
- const { title, value, onChange, linearHex, isLinearMode } = props;
112
- return (_jsx(TextInput, { disabled: linearHex ? !isLinearMode : false, className: classes.inputField, value: linearHex ? value.toLinearSpace().toHexString() : value.toHexString(), validator: ValidateColorHex, onChange: (val) => (linearHex ? onChange(Color3.FromHexString(val).toGammaSpace()) : onChange(Color3.FromHexString(val))), infoLabel: title
146
+ const { title, value, onChange, isLinear, ispropertyLinear } = props;
147
+ const displayMismatchesproperty = (isLinear ?? false) !== (ispropertyLinear ?? false);
148
+ const displaySpace = isLinear ? "linear" : "gamma";
149
+ const propertySpace = ispropertyLinear ? "linear" : "gamma";
150
+ const isColor4 = value instanceof Color4;
151
+ const colorClass = isColor4 ? "Color4" : "Color3";
152
+ return (_jsx(TextInput, { className: classes.inputField, value: value.toHexString(), validator: ValidateColorHex, validateOnlyOnBlur: true, onChange: (val) => onChange(colorFromHex(val, value)), infoLabel: title
113
153
  ? {
114
154
  label: title,
115
- // If not representing a linearHex, no info is needed.
116
- info: !props.linearHex ? undefined : !isLinearMode ? ( // If representing a linear hex but we are in gammaMode, simple message explaining why linearHex is disabled
117
- _jsx(_Fragment, { children: " This color picker is attached to an entity whose color is stored in gamma space, so we are showing linear hex in disabled view " })) : (
118
- // If representing a linear hex and we are in linearMode, give information about how to use these hex values
119
- _jsxs(_Fragment, { children: ["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 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 gamma space so that the color chosen here will match the color seen in your entity.", _jsx("br", {}), "If you want to copy/paste the HEX into your code, you can either use", _jsx(Body1Strong, { children: "Color3.FromHexString(LINEAR_HEX)" }), _jsx("br", {}), "or", _jsx("br", {}), _jsx(Body1Strong, { children: "Color3.FromHexString(GAMMA_HEX).toLinearSpace()" }), _jsx("br", {}), _jsx("br", {}), _jsx(Link, { url: "https://doc.babylonjs.com/preparingArtForBabylon/controllingColorSpace/", value: "Read more in our docs!" })] })),
155
+ info: (_jsxs(Body1, { children: ["This hex value is in ", _jsx(Body1Strong, { children: displaySpace }), " space", displayMismatchesproperty ? (_jsxs(Body1, { children: [", but the property stores its color in ", _jsx(Body1Strong, { children: propertySpace }), " space.", _jsx("br", {}), _jsx("br", {}), "The color picker converts automatically, but if you copy this hex into code you will need to convert it:", _jsx("br", {}), _jsxs(Body1Strong, { children: [colorClass, ".FromHexString(\"", value.toHexString(), "\").", isLinear ? "toGammaSpace()" : "toLinearSpace()"] })] })) : (_jsxs(Body1, { children: [", which matches the property's stored color space.", _jsx("br", {}), _jsx("br", {}), "To copy this hex into code, use", _jsx("br", {}), _jsxs(Body1Strong, { children: [colorClass, ".FromHexString(\"", value.toHexString(), "\")"] })] })), _jsx("br", {}), _jsx("br", {}), _jsx(Link, { url: "https://doc.babylonjs.com/preparingArtForBabylon/controllingColorSpace/", value: "Read more in our docs!" })] })),
120
156
  }
121
157
  : undefined }));
122
158
  };
123
159
  const InputRgbField = (props) => {
124
- const { value, onChange, title, rgbKey } = props;
160
+ const { value, onChange, title, rgbKey, isFloat } = props;
125
161
  const classes = useColorPickerStyles();
126
162
  const handleChange = useCallback((val) => {
127
163
  const newColor = value.clone();
128
- newColor[rgbKey] = val / 255.0; // Convert to 0-1 range
164
+ newColor[rgbKey] = isFloat ? val : val / 255.0;
129
165
  onChange(newColor);
130
- }, [value, onChange, rgbKey]);
131
- return (_jsx(SpinButton, { infoLabel: title ? { label: title } : undefined, className: classes.inputField, min: 0, max: 255, value: Math.round(value[rgbKey] * 255), forceInt: true, onChange: handleChange }));
166
+ }, [value, onChange, rgbKey, isFloat]);
167
+ return (_jsx(SpinButton, { infoLabel: title ? { label: title } : undefined, className: classes.inputField, min: 0, max: isFloat ? 1 : 255, value: isFloat ? value[rgbKey] : Math.round(value[rgbKey] * 255), step: isFloat ? 0.01 : 1, forceInt: !isFloat, precision: isFloat ? 4 : undefined, onChange: handleChange }, `${rgbKey}-${isFloat ? "float" : "int"}`));
132
168
  };
133
169
  function rgbaToHsv(color) {
134
170
  const c = new Color3(color.r, color.g, color.b);
135
171
  const hsv = c.toHSV();
136
172
  return { h: hsv.r, s: hsv.g, v: hsv.b, a: color.a };
137
173
  }
174
+ // Internal HSV ranges: H ∈ [0,360], S ∈ [0,1], V ∈ [0,1]
175
+ // Int mode display: H → 0-360, S → 0-100, V → 0-100
176
+ // Float mode display: H → 0-1, S → 0-1, V → 0-1
177
+ function getHsvDisplayParams(hsvKey, isFloat) {
178
+ if (isFloat) {
179
+ // All channels displayed as 0-1
180
+ const internalMax = hsvKey === "h" ? 360 : 1;
181
+ return { max: 1, toDisplay: (v) => v / internalMax, toInternal: (v) => v * internalMax, step: 0.01, forceInt: false, precision: 4 };
182
+ }
183
+ // Int mode
184
+ const scale = hsvKey === "h" ? 1 : 100;
185
+ const max = hsvKey === "h" ? 360 : 100;
186
+ return { max, toDisplay: (v) => Math.round(v * scale), toInternal: (v) => v / scale, step: 1, forceInt: true, precision: undefined };
187
+ }
138
188
  /**
139
189
  * 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.
140
190
  * 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.
@@ -142,19 +192,19 @@ function rgbaToHsv(color) {
142
192
  * @param props - The properties for the InputHsvField component.
143
193
  */
144
194
  export const InputHsvField = (props) => {
145
- const { value, title, hsvKey, max, onChange, scale = 1 } = props;
195
+ const { value, title, hsvKey, isFloat, onChange } = props;
146
196
  const classes = useColorPickerStyles();
197
+ const { max, toDisplay, toInternal, step, forceInt, precision } = getHsvDisplayParams(hsvKey, isFloat);
147
198
  const handleChange = useCallback((val) => {
148
- // Convert current color to HSV, update the new hsv value, then call onChange prop
149
199
  const hsv = rgbaToHsv(value);
150
- hsv[hsvKey] = val / scale;
200
+ hsv[hsvKey] = toInternal(val);
151
201
  let newColor = Color3.FromHSV(hsv.h, hsv.s, hsv.v);
152
202
  if (value instanceof Color4) {
153
203
  newColor = Color4.FromColor3(newColor, value.a ?? 1);
154
204
  }
155
205
  props.onChange(newColor);
156
- }, [value, onChange, hsvKey, scale]);
157
- return (_jsx(SpinButton, { infoLabel: title ? { label: title } : undefined, className: classes.inputField, min: 0, max: max, value: Math.round(rgbaToHsv(value)[hsvKey] * scale), forceInt: true, onChange: handleChange }));
206
+ }, [value, onChange, hsvKey, toInternal]);
207
+ return (_jsx(SpinButton, { infoLabel: title ? { label: title } : undefined, className: classes.inputField, min: 0, max: max, value: toDisplay(rgbaToHsv(value)[hsvKey]), step: step, forceInt: forceInt, precision: precision, onChange: handleChange }, `${hsvKey}-${isFloat ? "float" : "int"}`));
158
208
  };
159
209
  /**
160
210
  * 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).
@@ -171,15 +221,15 @@ const InputAlphaField = (props) => {
171
221
  if (color instanceof Color4) {
172
222
  const newColor = color.clone();
173
223
  newColor.a = value;
174
- return newColor;
224
+ onChange(newColor);
175
225
  }
176
226
  else {
177
- return Color4.FromColor3(color, value);
227
+ onChange(Color4.FromColor3(color, value));
178
228
  }
179
- }, [onChange]);
229
+ }, [color, onChange]);
180
230
  return (_jsx(SpinButton, { disabled: color instanceof Color3, min: 0, max: 1, className: classes.inputField, value: color instanceof Color3 ? 1 : color.a, step: 0.01, onChange: handleChange, infoLabel: {
181
231
  label: "Alpha",
182
- info: color instanceof Color3 ? (_jsx(_Fragment, { children: "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 property directly, either in code via entity.alpha OR via inspector's transparency section." })) : undefined,
232
+ info: color instanceof Color3 ? (_jsx(Body1, { children: "Because this color picker is representing a Color3, we do not permit modifying alpha from the color picker. You can however modify the property's alpha property directly, either in code via property.alpha OR via inspector's transparency section." })) : undefined,
183
233
  } }));
184
234
  };
185
235
  //# sourceMappingURL=colorPicker.js.map
@@ -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 { 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 trigger: {\r\n display: \"flex\",\r\n alignItems: \"center\",\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 className={classes.trigger}\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 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 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, useMemo } 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 trigger: {\r\n display: \"flex\",\r\n alignItems: \"center\",\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 isPropertyLinear = isLinearMode ?? false;\r\n\r\n /** Color in gamma space — used for visual elements (picker, preview, trigger) */\r\n const gammaColor = useMemo(() => (isPropertyLinear ? color.toGammaSpace() : color), [color, isPropertyLinear]);\r\n\r\n /** Color in the user-selected display space — used for numeric inputs (RGB, HSV, Hex) */\r\n const displayColor = useMemo(() => (isLinear === isPropertyLinear ? color : isLinear ? color.toLinearSpace() : color.toGammaSpace()), [color, isLinear, isPropertyLinear]);\r\n\r\n const handleColorPickerChange: FluentColorPickerProps[\"onColorChange\"] = (_, data) => {\r\n // The visual picker always operates in gamma space, convert back to property space\r\n let gammaResult: Color3 | Color4 = Color3.FromHSV(data.color.h, data.color.s, data.color.v);\r\n if (value instanceof Color4) {\r\n gammaResult = Color4.FromColor3(gammaResult, data.color.a ?? 1);\r\n }\r\n handleChange(isPropertyLinear ? gammaResult.toLinearSpace() : gammaResult);\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 const handleDisplayChange = (newDisplayColor: Color3 | Color4) => {\r\n const propertyColor = isLinear === isPropertyLinear ? newDisplayColor : isLinear ? newDisplayColor.toGammaSpace() : newDisplayColor.toLinearSpace();\r\n handleChange(propertyColor);\r\n };\r\n\r\n return (\r\n <Popover\r\n trigger={\r\n <ColorSwatch\r\n className={classes.trigger}\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={gammaColor.toHexString()}\r\n value={gammaColor.toHexString().slice(1)}\r\n />\r\n }\r\n >\r\n <div className={classes.container}>\r\n <FluentColorPicker className={classes.colorPicker} color={rgbaToHsv(gammaColor)} 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, Color Space, Data Type */}\r\n <div className={classes.row}>\r\n <div className={classes.previewColor} style={{ backgroundColor: gammaColor.toHexString() }} />\r\n <NumberDropdown\r\n className={classes.inputField}\r\n infoLabel={{\r\n label: \"Color Space\",\r\n info: (\r\n <Body1>\r\n Choose which color space to display numeric values in. This property stores its color in{\" \"}\r\n <Body1Strong>{isPropertyLinear ? \"linear\" : \"gamma\"}</Body1Strong> space. The visual picker always shows gamma (screen-accurate) colors.\r\n </Body1>\r\n ),\r\n }}\r\n options={[\r\n { label: \"Gamma\", value: 0 },\r\n { label: \"Linear\", value: 1 },\r\n ]}\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: (\r\n <Body1>\r\n <Body1Strong>Int</Body1Strong> displays RGB channels as integers in the 0–255 range. <Body1Strong>Float</Body1Strong> displays them as decimals\r\n in the 0–1 range. This is display-only and does not affect the stored color.\r\n </Body1>\r\n ),\r\n }}\r\n options={[\r\n { label: \"Int\", value: 0 },\r\n { label: \"Float\", value: 1 },\r\n ]}\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={displayColor} rgbKey=\"r\" isFloat={isFloat} onChange={handleDisplayChange} />\r\n <InputRgbField title=\"Green\" value={displayColor} rgbKey=\"g\" isFloat={isFloat} onChange={handleDisplayChange} />\r\n <InputRgbField title=\"Blue\" value={displayColor} rgbKey=\"b\" isFloat={isFloat} onChange={handleDisplayChange} />\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={displayColor} hsvKey=\"h\" isFloat={isFloat} onChange={handleDisplayChange} />\r\n <InputHsvField title=\"Saturation\" value={displayColor} hsvKey=\"s\" isFloat={isFloat} onChange={handleDisplayChange} />\r\n <InputHsvField title=\"Value\" value={displayColor} hsvKey=\"v\" isFloat={isFloat} onChange={handleDisplayChange} />\r\n </div>\r\n\r\n <div className={classes.inputRow}>\r\n <InputHexField title=\"Hexadecimal\" value={displayColor} isLinear={isLinear} ispropertyLinear={isPropertyLinear} onChange={handleDisplayChange} />\r\n </div>\r\n </div>\r\n </Popover>\r\n );\r\n});\r\n\r\nexport type InputHexProps = PrimitiveProps<Color3 | Color4> & {\r\n isLinear?: boolean;\r\n ispropertyLinear?: boolean;\r\n};\r\n\r\n/**\r\n * Converts a hex string to the same Color type as the original.\r\n * Supports \"#RGB\", \"#RRGGBB\", and \"#RRGGBBAA\" formats.\r\n * For Color4, honors alpha from \"#RRGGBBAA\" input or preserves the original alpha otherwise.\r\n * @param hex - The hex string to convert, in one of the supported formats.\r\n * @param original - The original color, used to determine whether to return a Color3 or Color4 and to preserve alpha if not specified in hex.\r\n * @returns A new Color3 or Color4 instance representing the hex color\r\n */\r\nfunction colorFromHex(hex: string, original: Color3 | Color4): Color3 | Color4 {\r\n const digits = hex.startsWith(\"#\") ? hex.slice(1) : hex;\r\n\r\n // Normalize short hex (RGB => RRGGBB)\r\n if (digits.length === 3) {\r\n hex = `#${digits[0]}${digits[0]}${digits[1]}${digits[1]}${digits[2]}${digits[2]}`;\r\n }\r\n\r\n // 8 hex digits = RRGGBBAA — use Color4.FromHexString which natively handles this\r\n if (digits.length === 8) {\r\n if (original instanceof Color4) {\r\n return Color4.FromHexString(hex);\r\n }\r\n return Color3.FromHexString(hex.slice(0, 7));\r\n }\r\n\r\n // 6 hex digits = RRGGBB (or normalized from 3)\r\n if (original instanceof Color4) {\r\n return Color4.FromColor3(Color3.FromHexString(hex), original.a);\r\n }\r\n return Color3.FromHexString(hex);\r\n}\r\n\r\n/**\r\n * Component which displays the passed in color's HEX value in the currently selected color space.\r\n * When the hex color is changed by user, component calculates the new Color3/4 value and calls onChange.\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, isLinear, ispropertyLinear } = props;\r\n\r\n const displayMismatchesproperty = (isLinear ?? false) !== (ispropertyLinear ?? false);\r\n const displaySpace = isLinear ? \"linear\" : \"gamma\";\r\n const propertySpace = ispropertyLinear ? \"linear\" : \"gamma\";\r\n const isColor4 = value instanceof Color4;\r\n const colorClass = isColor4 ? \"Color4\" : \"Color3\";\r\n\r\n return (\r\n <TextInput\r\n className={classes.inputField}\r\n value={value.toHexString()}\r\n validator={ValidateColorHex}\r\n validateOnlyOnBlur\r\n onChange={(val) => onChange(colorFromHex(val, value))}\r\n infoLabel={\r\n title\r\n ? {\r\n label: title,\r\n info: (\r\n <Body1>\r\n This hex value is in <Body1Strong>{displaySpace}</Body1Strong> space\r\n {displayMismatchesproperty ? (\r\n <Body1>\r\n , but the property stores its color in <Body1Strong>{propertySpace}</Body1Strong> space.\r\n <br />\r\n <br />\r\n The color picker converts automatically, but if you copy this hex into code you will need to convert it:\r\n <br />\r\n <Body1Strong>\r\n {colorClass}.FromHexString(\"{value.toHexString()}\").{isLinear ? \"toGammaSpace()\" : \"toLinearSpace()\"}\r\n </Body1Strong>\r\n </Body1>\r\n ) : (\r\n <Body1>\r\n , which matches the property's stored color space.\r\n <br />\r\n <br />\r\n To copy this hex into code, use\r\n <br />\r\n <Body1Strong>\r\n {colorClass}.FromHexString(\"{value.toHexString()}\")\r\n </Body1Strong>\r\n </Body1>\r\n )}\r\n <br />\r\n <br />\r\n <Link url=\"https://doc.babylonjs.com/preparingArtForBabylon/controllingColorSpace/\" value=\"Read more in our docs!\" />\r\n </Body1>\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 isFloat: boolean;\r\n};\r\n\r\nconst InputRgbField: FunctionComponent<InputRgbFieldProps> = (props) => {\r\n const { value, onChange, title, rgbKey, isFloat } = 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] = isFloat ? val : val / 255.0;\r\n onChange(newColor);\r\n },\r\n [value, onChange, rgbKey, isFloat]\r\n );\r\n\r\n return (\r\n <SpinButton\r\n key={`${rgbKey}-${isFloat ? \"float\" : \"int\"}`} // ensures remount when swapping between int/float, preserving min/max validation\r\n infoLabel={title ? { label: title } : undefined}\r\n className={classes.inputField}\r\n min={0}\r\n max={isFloat ? 1 : 255}\r\n value={isFloat ? value[rgbKey] : Math.round(value[rgbKey] * 255)}\r\n step={isFloat ? 0.01 : 1}\r\n forceInt={!isFloat}\r\n precision={isFloat ? 4 : undefined}\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 isFloat: boolean;\r\n};\r\n\r\n// Internal HSV ranges: H ∈ [0,360], S ∈ [0,1], V ∈ [0,1]\r\n// Int mode display: H → 0-360, S → 0-100, V → 0-100\r\n// Float mode display: H → 0-1, S → 0-1, V → 0-1\r\nfunction getHsvDisplayParams(hsvKey: HsvKey, isFloat: boolean) {\r\n if (isFloat) {\r\n // All channels displayed as 0-1\r\n const internalMax = hsvKey === \"h\" ? 360 : 1;\r\n return { max: 1, toDisplay: (v: number) => v / internalMax, toInternal: (v: number) => v * internalMax, step: 0.01, forceInt: false, precision: 4 };\r\n }\r\n // Int mode\r\n const scale = hsvKey === \"h\" ? 1 : 100;\r\n const max = hsvKey === \"h\" ? 360 : 100;\r\n return { max, toDisplay: (v: number) => Math.round(v * scale), toInternal: (v: number) => v / scale, step: 1, forceInt: true, precision: undefined };\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, isFloat, onChange } = props;\r\n\r\n const classes = useColorPickerStyles();\r\n const { max, toDisplay, toInternal, step, forceInt, precision } = getHsvDisplayParams(hsvKey, isFloat);\r\n\r\n const handleChange = useCallback(\r\n (val: number) => {\r\n const hsv = rgbaToHsv(value);\r\n hsv[hsvKey] = toInternal(val);\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, toInternal]\r\n );\r\n\r\n return (\r\n <SpinButton\r\n key={`${hsvKey}-${isFloat ? \"float\" : \"int\"}`} // ensures remount when swapping between int/float, preserving min/max validation\r\n infoLabel={title ? { label: title } : undefined}\r\n className={classes.inputField}\r\n min={0}\r\n max={max}\r\n value={toDisplay(rgbaToHsv(value)[hsvKey])}\r\n step={step}\r\n forceInt={forceInt}\r\n precision={precision}\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 onChange(newColor);\r\n } else {\r\n onChange(Color4.FromColor3(color, value));\r\n }\r\n },\r\n [color, 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 <Body1>\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 property's alpha\r\n property directly, either in code via property.alpha OR via inspector's transparency section.\r\n </Body1>\r\n ) : undefined,\r\n }}\r\n />\r\n );\r\n};\r\n"]}
@@ -203,7 +203,7 @@ export const SpinButton = forwardRef((props, ref) => {
203
203
  HandleOnBlur(event);
204
204
  }, [commitEditText, isDragging]);
205
205
  const contentBefore = !props.disableDragButton && !props.disabled && (isHovered || isDragging) && !isInputInvalid ? (_jsx(ArrowBidirectionalUpDownFilled, { className: classes.icon, style: { cursor: isDragging ? "ns-resize" : "pointer" }, onPointerDown: handleIconPointerDown, onPointerMove: handleIconPointerMove, onPointerUp: handleIconPointerUp })) : undefined;
206
- const input = (_jsx("div", { className: props.infoLabel ? undefined : props.className, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => {
206
+ const input = (_jsx("div", { className: props.infoLabel ? undefined : props.className, onPointerEnter: (e) => e.pointerType === "mouse" && setIsHovered(true), onPointerLeave: () => {
207
207
  if (!isDragging) {
208
208
  setIsHovered(false);
209
209
  }
@@ -1 +1 @@
1
- {"version":3,"file":"spinButton.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/spinButton.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ChangeEvent, FocusEvent, KeyboardEvent, PointerEvent } from \"react\";\r\n\r\nimport type { PrimitiveProps } from \"./primitive\";\r\n\r\nimport { Input, makeStyles, mergeClasses, tokens, useId, useMergedRefs } from \"@fluentui/react-components\";\r\nimport { ArrowBidirectionalUpDownFilled } from \"@fluentui/react-icons\";\r\n\r\nimport { Clamp } from \"core/Maths/math.scalar.functions\";\r\nimport { forwardRef, useCallback, useContext, useEffect, useRef, useState } from \"react\";\r\nimport { ToolContext } from \"../hoc/fluentToolWrapper\";\r\nimport { useKeyState } from \"../hooks/keyboardHooks\";\r\nimport { InfoLabel } from \"./infoLabel\";\r\nimport { CalculatePrecision, HandleKeyDown, HandleOnBlur, useInputStyles } from \"./utils\";\r\n\r\nfunction CoerceStepValue(step: number, isFineKeyPressed: boolean, isCourseKeyPressed: boolean): number {\r\n // When the fine key is pressed, decrease step by a factor of 10.\r\n if (isFineKeyPressed) {\r\n return step * 0.1;\r\n }\r\n\r\n // When the course key is pressed, increase step by a factor of 10.\r\n if (isCourseKeyPressed) {\r\n return step * 10;\r\n }\r\n\r\n return step;\r\n}\r\n\r\n// Allow arbitrary expressions, primarily for math operations (e.g. 10*60 for 10 minutes in seconds).\r\n// Use Function constructor to safely evaluate the expression without allowing access to scope.\r\n// If the expression is invalid, fallback to NaN which will be caught by validateValue and prevent committing.\r\nfunction EvaluateExpression(rawValue: string): number {\r\n const val = rawValue.trim();\r\n try {\r\n return Number(Function(`\"use strict\";return (${val})`)());\r\n } catch {\r\n return NaN;\r\n }\r\n}\r\n\r\nexport type SpinButtonProps = PrimitiveProps<number> & {\r\n min?: number;\r\n max?: number;\r\n /** Determines how much the spinbutton increments with the arrow keys. Note this also determines the precision value (# of decimals in display value)\r\n * i.e. if step = 1, precision = 0. step = 0.0089, precision = 4. step = 300, precision = 2. step = 23.00, precision = 2. */\r\n step?: number;\r\n unit?: string;\r\n forceInt?: boolean;\r\n validator?: (value: number) => boolean;\r\n /** Optional fixed precision (number of decimal digits). Overrides the automatically computed display precision. */\r\n precision?: number;\r\n /** Optional className for the input element */\r\n inputClassName?: string;\r\n /** When true, hides the drag-to-scrub button */\r\n disableDragButton?: boolean;\r\n};\r\n\r\nconst useStyles = makeStyles({\r\n icon: {\r\n \"&:hover\": {\r\n color: tokens.colorBrandForeground1,\r\n },\r\n },\r\n});\r\n\r\n/**\r\n * A numeric input with a vertical drag-to-scrub icon (ArrowsBidirectionalRegular rotated 90°).\r\n * Click-and-drag up/down on the icon to increment/decrement the value.\r\n */\r\nexport const SpinButton = forwardRef<HTMLInputElement, SpinButtonProps>((props, ref) => {\r\n SpinButton.displayName = \"SpinButton2\";\r\n const inputClasses = useInputStyles();\r\n const classes = useStyles();\r\n const { size } = useContext(ToolContext);\r\n\r\n const { min, max } = props;\r\n const baseStep = props.step ?? 1;\r\n\r\n // Local ref for the input element so we can blur it programmatically (e.g. when a drag starts while editing).\r\n const inputRef = useRef<HTMLInputElement | null>(null);\r\n const mergedRef = useMergedRefs(ref, inputRef);\r\n\r\n // Modifier keys for step coercion.\r\n const isAltKeyPressed = useKeyState(\"Alt\", { preventDefault: true });\r\n const isShiftKeyPressed = useKeyState(\"Shift\");\r\n\r\n const step = CoerceStepValue(baseStep, isAltKeyPressed, isShiftKeyPressed);\r\n const stepPrecision = Math.max(0, CalculatePrecision(step));\r\n\r\n const [value, setValue] = useState<number>(props.value ?? 0);\r\n const lastCommittedValue = useRef(props.value);\r\n const [isDragging, setIsDragging] = useState(false);\r\n const scrubStartYRef = useRef(0);\r\n const scrubStartValueRef = useRef(0);\r\n const lastPointerYRef = useRef(0);\r\n const [isHovered, setIsHovered] = useState(false);\r\n\r\n // Editing state: when the user is typing, we show their raw text rather than the formatted value.\r\n const [isEditing, setIsEditing] = useState(false);\r\n const [editText, setEditText] = useState(\"\");\r\n\r\n const valuePrecision = Math.max(0, CalculatePrecision(value));\r\n // Display precision: controls how many decimals are shown in the formatted displayValue. Cap at 4 to avoid wild numbers.\r\n // If a fixed precision prop is provided, use it instead.\r\n const displayPrecision = props.precision ?? Math.min(4, Math.max(stepPrecision, valuePrecision));\r\n\r\n // Format a number for display: toFixed, then trim trailing zeros and period unless a fixed precision is specified.\r\n const formatValue = useCallback(\r\n (v: number) => {\r\n const fixed = v.toFixed(displayPrecision);\r\n if (props.precision !== undefined) {\r\n return fixed;\r\n }\r\n return fixed.replace(/(\\.\\d*?)0+$/, \"$1\").replace(/\\.$/, \"\");\r\n },\r\n [displayPrecision, props.precision]\r\n );\r\n\r\n useEffect(() => {\r\n if (!isDragging && props.value !== lastCommittedValue.current) {\r\n lastCommittedValue.current = props.value;\r\n setValue(props.value ?? 0);\r\n }\r\n }, [props.value, isDragging]);\r\n\r\n const validateValue = useCallback(\r\n (numericValue: number): boolean => {\r\n const outOfBounds = (min !== undefined && numericValue < min) || (max !== undefined && numericValue > max);\r\n const failsValidator = props.validator && !props.validator(numericValue);\r\n const failsIntCheck = props.forceInt ? !Number.isInteger(numericValue) : false;\r\n const invalid = !!outOfBounds || !!failsValidator || isNaN(numericValue) || !!failsIntCheck;\r\n return !invalid;\r\n },\r\n [min, max, props.validator, props.forceInt]\r\n );\r\n\r\n // Constrain a value to the valid range by clamping to [min, max].\r\n const constrainValue = useCallback((v: number) => Clamp(v, min ?? -Infinity, max ?? Infinity), [min, max]);\r\n\r\n const tryCommitValue = useCallback(\r\n (currVal: number) => {\r\n if (validateValue(currVal) && currVal !== lastCommittedValue.current) {\r\n lastCommittedValue.current = currVal;\r\n props.onChange(currVal);\r\n }\r\n },\r\n [validateValue, props.onChange]\r\n );\r\n\r\n const handleInputChange = useCallback((_: ChangeEvent, data: { value: string }) => {\r\n // Just update the raw text — no evaluation or commit until Enter/blur.\r\n setEditText(data.value);\r\n }, []);\r\n\r\n // Evaluate the current edit text and commit the value. Returns the clamped value if valid, or undefined.\r\n const commitEditText = useCallback(\r\n (text: string): number | undefined => {\r\n const numericValue = EvaluateExpression(text);\r\n if (!isNaN(numericValue) && validateValue(numericValue)) {\r\n const constrained = constrainValue(numericValue);\r\n setValue(constrained);\r\n tryCommitValue(constrained);\r\n return constrained;\r\n }\r\n return undefined;\r\n },\r\n [validateValue, constrainValue, tryCommitValue]\r\n );\r\n\r\n const handleIconPointerDown = useCallback(\r\n (e: PointerEvent<Element>) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n // If the input was being edited, commit the current text and blur the input\r\n // so the focus state stays consistent after the drag ends.\r\n let startValue = value;\r\n if (isEditing) {\r\n const committed = commitEditText(editText);\r\n if (committed !== undefined) {\r\n startValue = committed;\r\n }\r\n setIsEditing(false);\r\n }\r\n // Blur the active element to ensure we can observe document level modifier keys.\r\n (inputRef.current?.ownerDocument.activeElement as Partial<HTMLElement>)?.blur?.();\r\n setIsDragging(true);\r\n scrubStartYRef.current = e.clientY;\r\n scrubStartValueRef.current = startValue;\r\n e.currentTarget.setPointerCapture(e.pointerId);\r\n },\r\n [value, isEditing, editText, commitEditText]\r\n );\r\n\r\n // When the step size changes during a drag (e.g. Shift/Alt pressed or released), reset the scrub reference point\r\n // to the current value and pointer position so only future movement uses the new step.\r\n useEffect(() => {\r\n if (isDragging) {\r\n scrubStartValueRef.current = value;\r\n scrubStartYRef.current = lastPointerYRef.current;\r\n }\r\n }, [step]);\r\n\r\n const handleIconPointerMove = useCallback(\r\n (e: PointerEvent) => {\r\n if (!isDragging) {\r\n return;\r\n }\r\n lastPointerYRef.current = e.clientY;\r\n // Dragging up (negative dy) should increment, dragging down should decrement.\r\n // Scale delta by step but round to display precision (not step) for smooth fine-grained control.\r\n const dy = scrubStartYRef.current - e.clientY;\r\n // 5 is just a number that \"feels right\" for the drag sensitivity — it determines how far the user needs to drag to change the value by 1 step.\r\n const delta = (dy * step) / 5;\r\n const raw = scrubStartValueRef.current + delta;\r\n const precisionFactor = Math.pow(10, displayPrecision);\r\n const rounded = Math.round(raw * precisionFactor) / precisionFactor;\r\n const constrained = constrainValue(rounded);\r\n setValue(constrained);\r\n tryCommitValue(constrained);\r\n },\r\n [isDragging, step, displayPrecision, constrainValue, tryCommitValue]\r\n );\r\n\r\n const handleIconPointerUp = useCallback((e: PointerEvent<Element>) => {\r\n setIsDragging(false);\r\n e.currentTarget.releasePointerCapture(e.pointerId);\r\n }, []);\r\n\r\n const handleKeyDown = useCallback(\r\n (event: KeyboardEvent<HTMLInputElement>) => {\r\n // Commit on Enter and blur the input if the value is valid.\r\n if (event.key === \"Enter\") {\r\n const committed = commitEditText(event.currentTarget.value);\r\n if (committed !== undefined) {\r\n inputRef.current?.blur();\r\n }\r\n }\r\n\r\n if (event.key === \"ArrowUp\" || event.key === \"ArrowDown\") {\r\n event.preventDefault();\r\n const direction = event.key === \"ArrowUp\" ? 1 : -1;\r\n const newValue = constrainValue(Math.round((value + direction * step) / step) * step);\r\n setValue(newValue);\r\n tryCommitValue(newValue);\r\n // Update edit text to reflect the new value so the user sees the change\r\n setEditText(formatValue(newValue));\r\n }\r\n\r\n HandleKeyDown(event);\r\n },\r\n [value, step, constrainValue, tryCommitValue, commitEditText, formatValue]\r\n );\r\n\r\n const id = useId(\"spin-button2\");\r\n\r\n // Real-time validation: when editing, validate the expression; otherwise validate the committed value.\r\n // (validateValue already handles NaN, so no separate isNaN check needed.)\r\n const isInputInvalid = !validateValue(isEditing ? EvaluateExpression(editText) : value);\r\n\r\n const mergedClassName = mergeClasses(inputClasses.inputFill, isInputInvalid ? inputClasses.invalid : \"\");\r\n const inputSlotClassName = mergeClasses(inputClasses.inputSlot, props.inputClassName);\r\n\r\n const formattedValue = formatValue(value);\r\n\r\n const handleFocus = useCallback(() => {\r\n setIsEditing(true);\r\n setEditText(formattedValue);\r\n }, [formattedValue]);\r\n\r\n const handleBlur = useCallback(\r\n (event: FocusEvent<HTMLInputElement>) => {\r\n // Skip blur handling if a drag just started (icon pointerDown already committed).\r\n if (isDragging) {\r\n return;\r\n }\r\n commitEditText(event.target.value);\r\n setIsEditing(false);\r\n HandleOnBlur(event);\r\n },\r\n [commitEditText, isDragging]\r\n );\r\n\r\n const contentBefore =\r\n !props.disableDragButton && !props.disabled && (isHovered || isDragging) && !isInputInvalid ? (\r\n <ArrowBidirectionalUpDownFilled\r\n className={classes.icon}\r\n style={{ cursor: isDragging ? \"ns-resize\" : \"pointer\" }}\r\n onPointerDown={handleIconPointerDown}\r\n onPointerMove={handleIconPointerMove}\r\n onPointerUp={handleIconPointerUp}\r\n />\r\n ) : undefined;\r\n\r\n const input = (\r\n <div\r\n className={props.infoLabel ? undefined : props.className}\r\n onMouseEnter={() => setIsHovered(true)}\r\n onMouseLeave={() => {\r\n if (!isDragging) {\r\n setIsHovered(false);\r\n }\r\n }}\r\n >\r\n <Input\r\n ref={mergedRef}\r\n id={id}\r\n appearance=\"outline\"\r\n size={size}\r\n className={mergedClassName}\r\n input={{ className: inputSlotClassName }}\r\n value={isEditing ? editText : formattedValue}\r\n disabled={props.disabled}\r\n onChange={handleInputChange}\r\n onFocus={handleFocus}\r\n onKeyDown={handleKeyDown}\r\n onBlur={handleBlur}\r\n contentBefore={contentBefore}\r\n contentAfter={props.unit}\r\n />\r\n </div>\r\n );\r\n\r\n return props.infoLabel ? (\r\n <div className={mergeClasses(inputClasses.container, props.className)}>\r\n <InfoLabel {...props.infoLabel} htmlFor={id} />\r\n {input}\r\n </div>\r\n ) : (\r\n input\r\n );\r\n});\r\n"]}
1
+ {"version":3,"file":"spinButton.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/spinButton.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ChangeEvent, FocusEvent, KeyboardEvent, PointerEvent } from \"react\";\r\n\r\nimport type { PrimitiveProps } from \"./primitive\";\r\n\r\nimport { Input, makeStyles, mergeClasses, tokens, useId, useMergedRefs } from \"@fluentui/react-components\";\r\nimport { ArrowBidirectionalUpDownFilled } from \"@fluentui/react-icons\";\r\n\r\nimport { Clamp } from \"core/Maths/math.scalar.functions\";\r\nimport { forwardRef, useCallback, useContext, useEffect, useRef, useState } from \"react\";\r\nimport { ToolContext } from \"../hoc/fluentToolWrapper\";\r\nimport { useKeyState } from \"../hooks/keyboardHooks\";\r\nimport { InfoLabel } from \"./infoLabel\";\r\nimport { CalculatePrecision, HandleKeyDown, HandleOnBlur, useInputStyles } from \"./utils\";\r\n\r\nfunction CoerceStepValue(step: number, isFineKeyPressed: boolean, isCourseKeyPressed: boolean): number {\r\n // When the fine key is pressed, decrease step by a factor of 10.\r\n if (isFineKeyPressed) {\r\n return step * 0.1;\r\n }\r\n\r\n // When the course key is pressed, increase step by a factor of 10.\r\n if (isCourseKeyPressed) {\r\n return step * 10;\r\n }\r\n\r\n return step;\r\n}\r\n\r\n// Allow arbitrary expressions, primarily for math operations (e.g. 10*60 for 10 minutes in seconds).\r\n// Use Function constructor to safely evaluate the expression without allowing access to scope.\r\n// If the expression is invalid, fallback to NaN which will be caught by validateValue and prevent committing.\r\nfunction EvaluateExpression(rawValue: string): number {\r\n const val = rawValue.trim();\r\n try {\r\n return Number(Function(`\"use strict\";return (${val})`)());\r\n } catch {\r\n return NaN;\r\n }\r\n}\r\n\r\nexport type SpinButtonProps = PrimitiveProps<number> & {\r\n min?: number;\r\n max?: number;\r\n /** Determines how much the spinbutton increments with the arrow keys. Note this also determines the precision value (# of decimals in display value)\r\n * i.e. if step = 1, precision = 0. step = 0.0089, precision = 4. step = 300, precision = 2. step = 23.00, precision = 2. */\r\n step?: number;\r\n unit?: string;\r\n forceInt?: boolean;\r\n validator?: (value: number) => boolean;\r\n /** Optional fixed precision (number of decimal digits). Overrides the automatically computed display precision. */\r\n precision?: number;\r\n /** Optional className for the input element */\r\n inputClassName?: string;\r\n /** When true, hides the drag-to-scrub button */\r\n disableDragButton?: boolean;\r\n};\r\n\r\nconst useStyles = makeStyles({\r\n icon: {\r\n \"&:hover\": {\r\n color: tokens.colorBrandForeground1,\r\n },\r\n },\r\n});\r\n\r\n/**\r\n * A numeric input with a vertical drag-to-scrub icon (ArrowsBidirectionalRegular rotated 90°).\r\n * Click-and-drag up/down on the icon to increment/decrement the value.\r\n */\r\nexport const SpinButton = forwardRef<HTMLInputElement, SpinButtonProps>((props, ref) => {\r\n SpinButton.displayName = \"SpinButton2\";\r\n const inputClasses = useInputStyles();\r\n const classes = useStyles();\r\n const { size } = useContext(ToolContext);\r\n\r\n const { min, max } = props;\r\n const baseStep = props.step ?? 1;\r\n\r\n // Local ref for the input element so we can blur it programmatically (e.g. when a drag starts while editing).\r\n const inputRef = useRef<HTMLInputElement | null>(null);\r\n const mergedRef = useMergedRefs(ref, inputRef);\r\n\r\n // Modifier keys for step coercion.\r\n const isAltKeyPressed = useKeyState(\"Alt\", { preventDefault: true });\r\n const isShiftKeyPressed = useKeyState(\"Shift\");\r\n\r\n const step = CoerceStepValue(baseStep, isAltKeyPressed, isShiftKeyPressed);\r\n const stepPrecision = Math.max(0, CalculatePrecision(step));\r\n\r\n const [value, setValue] = useState<number>(props.value ?? 0);\r\n const lastCommittedValue = useRef(props.value);\r\n const [isDragging, setIsDragging] = useState(false);\r\n const scrubStartYRef = useRef(0);\r\n const scrubStartValueRef = useRef(0);\r\n const lastPointerYRef = useRef(0);\r\n const [isHovered, setIsHovered] = useState(false);\r\n\r\n // Editing state: when the user is typing, we show their raw text rather than the formatted value.\r\n const [isEditing, setIsEditing] = useState(false);\r\n const [editText, setEditText] = useState(\"\");\r\n\r\n const valuePrecision = Math.max(0, CalculatePrecision(value));\r\n // Display precision: controls how many decimals are shown in the formatted displayValue. Cap at 4 to avoid wild numbers.\r\n // If a fixed precision prop is provided, use it instead.\r\n const displayPrecision = props.precision ?? Math.min(4, Math.max(stepPrecision, valuePrecision));\r\n\r\n // Format a number for display: toFixed, then trim trailing zeros and period unless a fixed precision is specified.\r\n const formatValue = useCallback(\r\n (v: number) => {\r\n const fixed = v.toFixed(displayPrecision);\r\n if (props.precision !== undefined) {\r\n return fixed;\r\n }\r\n return fixed.replace(/(\\.\\d*?)0+$/, \"$1\").replace(/\\.$/, \"\");\r\n },\r\n [displayPrecision, props.precision]\r\n );\r\n\r\n useEffect(() => {\r\n if (!isDragging && props.value !== lastCommittedValue.current) {\r\n lastCommittedValue.current = props.value;\r\n setValue(props.value ?? 0);\r\n }\r\n }, [props.value, isDragging]);\r\n\r\n const validateValue = useCallback(\r\n (numericValue: number): boolean => {\r\n const outOfBounds = (min !== undefined && numericValue < min) || (max !== undefined && numericValue > max);\r\n const failsValidator = props.validator && !props.validator(numericValue);\r\n const failsIntCheck = props.forceInt ? !Number.isInteger(numericValue) : false;\r\n const invalid = !!outOfBounds || !!failsValidator || isNaN(numericValue) || !!failsIntCheck;\r\n return !invalid;\r\n },\r\n [min, max, props.validator, props.forceInt]\r\n );\r\n\r\n // Constrain a value to the valid range by clamping to [min, max].\r\n const constrainValue = useCallback((v: number) => Clamp(v, min ?? -Infinity, max ?? Infinity), [min, max]);\r\n\r\n const tryCommitValue = useCallback(\r\n (currVal: number) => {\r\n if (validateValue(currVal) && currVal !== lastCommittedValue.current) {\r\n lastCommittedValue.current = currVal;\r\n props.onChange(currVal);\r\n }\r\n },\r\n [validateValue, props.onChange]\r\n );\r\n\r\n const handleInputChange = useCallback((_: ChangeEvent, data: { value: string }) => {\r\n // Just update the raw text — no evaluation or commit until Enter/blur.\r\n setEditText(data.value);\r\n }, []);\r\n\r\n // Evaluate the current edit text and commit the value. Returns the clamped value if valid, or undefined.\r\n const commitEditText = useCallback(\r\n (text: string): number | undefined => {\r\n const numericValue = EvaluateExpression(text);\r\n if (!isNaN(numericValue) && validateValue(numericValue)) {\r\n const constrained = constrainValue(numericValue);\r\n setValue(constrained);\r\n tryCommitValue(constrained);\r\n return constrained;\r\n }\r\n return undefined;\r\n },\r\n [validateValue, constrainValue, tryCommitValue]\r\n );\r\n\r\n const handleIconPointerDown = useCallback(\r\n (e: PointerEvent<Element>) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n // If the input was being edited, commit the current text and blur the input\r\n // so the focus state stays consistent after the drag ends.\r\n let startValue = value;\r\n if (isEditing) {\r\n const committed = commitEditText(editText);\r\n if (committed !== undefined) {\r\n startValue = committed;\r\n }\r\n setIsEditing(false);\r\n }\r\n // Blur the active element to ensure we can observe document level modifier keys.\r\n (inputRef.current?.ownerDocument.activeElement as Partial<HTMLElement>)?.blur?.();\r\n setIsDragging(true);\r\n scrubStartYRef.current = e.clientY;\r\n scrubStartValueRef.current = startValue;\r\n e.currentTarget.setPointerCapture(e.pointerId);\r\n },\r\n [value, isEditing, editText, commitEditText]\r\n );\r\n\r\n // When the step size changes during a drag (e.g. Shift/Alt pressed or released), reset the scrub reference point\r\n // to the current value and pointer position so only future movement uses the new step.\r\n useEffect(() => {\r\n if (isDragging) {\r\n scrubStartValueRef.current = value;\r\n scrubStartYRef.current = lastPointerYRef.current;\r\n }\r\n }, [step]);\r\n\r\n const handleIconPointerMove = useCallback(\r\n (e: PointerEvent) => {\r\n if (!isDragging) {\r\n return;\r\n }\r\n lastPointerYRef.current = e.clientY;\r\n // Dragging up (negative dy) should increment, dragging down should decrement.\r\n // Scale delta by step but round to display precision (not step) for smooth fine-grained control.\r\n const dy = scrubStartYRef.current - e.clientY;\r\n // 5 is just a number that \"feels right\" for the drag sensitivity — it determines how far the user needs to drag to change the value by 1 step.\r\n const delta = (dy * step) / 5;\r\n const raw = scrubStartValueRef.current + delta;\r\n const precisionFactor = Math.pow(10, displayPrecision);\r\n const rounded = Math.round(raw * precisionFactor) / precisionFactor;\r\n const constrained = constrainValue(rounded);\r\n setValue(constrained);\r\n tryCommitValue(constrained);\r\n },\r\n [isDragging, step, displayPrecision, constrainValue, tryCommitValue]\r\n );\r\n\r\n const handleIconPointerUp = useCallback((e: PointerEvent<Element>) => {\r\n setIsDragging(false);\r\n e.currentTarget.releasePointerCapture(e.pointerId);\r\n }, []);\r\n\r\n const handleKeyDown = useCallback(\r\n (event: KeyboardEvent<HTMLInputElement>) => {\r\n // Commit on Enter and blur the input if the value is valid.\r\n if (event.key === \"Enter\") {\r\n const committed = commitEditText(event.currentTarget.value);\r\n if (committed !== undefined) {\r\n inputRef.current?.blur();\r\n }\r\n }\r\n\r\n if (event.key === \"ArrowUp\" || event.key === \"ArrowDown\") {\r\n event.preventDefault();\r\n const direction = event.key === \"ArrowUp\" ? 1 : -1;\r\n const newValue = constrainValue(Math.round((value + direction * step) / step) * step);\r\n setValue(newValue);\r\n tryCommitValue(newValue);\r\n // Update edit text to reflect the new value so the user sees the change\r\n setEditText(formatValue(newValue));\r\n }\r\n\r\n HandleKeyDown(event);\r\n },\r\n [value, step, constrainValue, tryCommitValue, commitEditText, formatValue]\r\n );\r\n\r\n const id = useId(\"spin-button2\");\r\n\r\n // Real-time validation: when editing, validate the expression; otherwise validate the committed value.\r\n // (validateValue already handles NaN, so no separate isNaN check needed.)\r\n const isInputInvalid = !validateValue(isEditing ? EvaluateExpression(editText) : value);\r\n\r\n const mergedClassName = mergeClasses(inputClasses.inputFill, isInputInvalid ? inputClasses.invalid : \"\");\r\n const inputSlotClassName = mergeClasses(inputClasses.inputSlot, props.inputClassName);\r\n\r\n const formattedValue = formatValue(value);\r\n\r\n const handleFocus = useCallback(() => {\r\n setIsEditing(true);\r\n setEditText(formattedValue);\r\n }, [formattedValue]);\r\n\r\n const handleBlur = useCallback(\r\n (event: FocusEvent<HTMLInputElement>) => {\r\n // Skip blur handling if a drag just started (icon pointerDown already committed).\r\n if (isDragging) {\r\n return;\r\n }\r\n commitEditText(event.target.value);\r\n setIsEditing(false);\r\n HandleOnBlur(event);\r\n },\r\n [commitEditText, isDragging]\r\n );\r\n\r\n const contentBefore =\r\n !props.disableDragButton && !props.disabled && (isHovered || isDragging) && !isInputInvalid ? (\r\n <ArrowBidirectionalUpDownFilled\r\n className={classes.icon}\r\n style={{ cursor: isDragging ? \"ns-resize\" : \"pointer\" }}\r\n onPointerDown={handleIconPointerDown}\r\n onPointerMove={handleIconPointerMove}\r\n onPointerUp={handleIconPointerUp}\r\n />\r\n ) : undefined;\r\n\r\n const input = (\r\n <div\r\n className={props.infoLabel ? undefined : props.className}\r\n onPointerEnter={(e) => e.pointerType === \"mouse\" && setIsHovered(true)}\r\n onPointerLeave={() => {\r\n if (!isDragging) {\r\n setIsHovered(false);\r\n }\r\n }}\r\n >\r\n <Input\r\n ref={mergedRef}\r\n id={id}\r\n appearance=\"outline\"\r\n size={size}\r\n className={mergedClassName}\r\n input={{ className: inputSlotClassName }}\r\n value={isEditing ? editText : formattedValue}\r\n disabled={props.disabled}\r\n onChange={handleInputChange}\r\n onFocus={handleFocus}\r\n onKeyDown={handleKeyDown}\r\n onBlur={handleBlur}\r\n contentBefore={contentBefore}\r\n contentAfter={props.unit}\r\n />\r\n </div>\r\n );\r\n\r\n return props.infoLabel ? (\r\n <div className={mergeClasses(inputClasses.container, props.className)}>\r\n <InfoLabel {...props.infoLabel} htmlFor={id} />\r\n {input}\r\n </div>\r\n ) : (\r\n input\r\n );\r\n});\r\n"]}
@@ -40,6 +40,13 @@ export const TextInput = (props) => {
40
40
  tryCommitValue(event.currentTarget.value);
41
41
  }
42
42
  };
43
+ const handleKeyDown = (event) => {
44
+ HandleKeyDown(event);
45
+ // When validateOnlyOnBlur is set, also commit on Enter for better UX
46
+ if (event.key === "Enter" && props.validateOnlyOnBlur) {
47
+ tryCommitValue(event.currentTarget.value);
48
+ }
49
+ };
43
50
  const handleBlur = (event) => {
44
51
  HandleOnBlur(event);
45
52
  if (props.validateOnlyOnBlur) {
@@ -48,6 +55,6 @@ export const TextInput = (props) => {
48
55
  };
49
56
  const mergedClassName = mergeClasses(classes.inputFill, !validateValue(value) ? classes.invalid : "");
50
57
  const id = useId("input-button");
51
- return (_jsxs("div", { className: mergeClasses(classes.container, props.className), children: [props.infoLabel && _jsx(InfoLabel, { ...props.infoLabel, htmlFor: id }), _jsx(FluentInput, { ...props, input: { className: classes.inputSlot }, id: id, size: size, value: value, onChange: handleChange, onKeyUp: handleKeyUp, onKeyDown: HandleKeyDown, onBlur: handleBlur, className: mergedClassName })] }));
58
+ return (_jsxs("div", { className: mergeClasses(classes.container, props.className), children: [props.infoLabel && _jsx(InfoLabel, { ...props.infoLabel, htmlFor: id }), _jsx(FluentInput, { ...props, input: { className: classes.inputSlot }, id: id, size: size, value: value, onChange: handleChange, onKeyUp: handleKeyUp, onKeyDown: handleKeyDown, onBlur: handleBlur, className: mergedClassName })] }));
52
59
  };
53
60
  //# sourceMappingURL=textInput.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"textInput.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/textInput.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEhE,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAEvF,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAOvD,MAAM,CAAC,MAAM,SAAS,GAAsC,CAAC,KAAK,EAAE,EAAE;IAClE,SAAS,CAAC,WAAW,GAAG,WAAW,CAAC;IACpC,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,kBAAkB,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACzC,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,KAAK,CAAC,KAAK,KAAK,kBAAkB,CAAC,OAAO,EAAE,CAAC;YAC7C,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,8CAA8C;YACrE,kBAAkB,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC;QAC7C,CAAC;IACL,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAElB,MAAM,aAAa,GAAG,CAAC,GAAW,EAAW,EAAE;QAC3C,MAAM,cAAc,GAAG,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAChE,OAAO,CAAC,cAAc,CAAC;IAC3B,CAAC,CAAC;IAEF,MAAM,cAAc,GAAG,CAAC,OAAe,EAAE,EAAE;QACvC,+DAA+D;QAC/D,IAAI,aAAa,CAAC,OAAO,CAAC,IAAI,OAAO,KAAK,kBAAkB,CAAC,OAAO,EAAE,CAAC;YACnE,kBAAkB,CAAC,OAAO,GAAG,OAAO,CAAC;YACrC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CAAC,KAAoC,EAAE,IAAuB,EAAE,EAAE;QACnF,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;YAC5B,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,KAAsC,EAAE,EAAE;QAC3D,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;YAC5B,cAAc,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;IACL,CAAC,CAAC;IACF,MAAM,UAAU,GAAG,CAAC,KAAmC,EAAE,EAAE;QACvD,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,IAAI,KAAK,CAAC,kBAAkB,EAAE,CAAC;YAC3B,cAAc,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,eAAe,GAAG,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAEtG,MAAM,EAAE,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC;IACjC,OAAO,CACH,eAAK,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,aAC3D,KAAK,CAAC,SAAS,IAAI,KAAC,SAAS,OAAK,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,GAAI,EACnE,KAAC,WAAW,OACJ,KAAK,EACT,KAAK,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,EACvC,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,aAAa,EACxB,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,eAAe,GAC5B,IACA,CACT,CAAC;AACN,CAAC,CAAC","sourcesContent":["import type { FunctionComponent, KeyboardEvent, ChangeEvent, FocusEvent } from \"react\";\r\nimport { useContext, useEffect, useRef, useState } from \"react\";\r\nimport type { InputOnChangeData } from \"@fluentui/react-components\";\r\nimport { Input as FluentInput, mergeClasses, useId } from \"@fluentui/react-components\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\nimport { InfoLabel } from \"./infoLabel\";\r\nimport { HandleKeyDown, HandleOnBlur, useInputStyles } from \"./utils\";\r\nimport { ToolContext } from \"../hoc/fluentToolWrapper\";\r\n\r\nexport type TextInputProps = PrimitiveProps<string> & {\r\n validator?: (value: string) => boolean;\r\n validateOnlyOnBlur?: boolean;\r\n};\r\n\r\nexport const TextInput: FunctionComponent<TextInputProps> = (props) => {\r\n TextInput.displayName = \"TextInput\";\r\n const classes = useInputStyles();\r\n const [value, setValue] = useState(props.value);\r\n const lastCommittedValue = useRef(props.value);\r\n const { size } = useContext(ToolContext);\r\n useEffect(() => {\r\n if (props.value !== lastCommittedValue.current) {\r\n setValue(props.value); // Update local state when props.value changes\r\n lastCommittedValue.current = props.value;\r\n }\r\n }, [props.value]);\r\n\r\n const validateValue = (val: string): boolean => {\r\n const failsValidator = props.validator && !props.validator(val);\r\n return !failsValidator;\r\n };\r\n\r\n const tryCommitValue = (currVal: string) => {\r\n // Only commit if valid and different from last committed value\r\n if (validateValue(currVal) && currVal !== lastCommittedValue.current) {\r\n lastCommittedValue.current = currVal;\r\n props.onChange(currVal);\r\n }\r\n };\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>, data: InputOnChangeData) => {\r\n event.stopPropagation();\r\n setValue(data.value);\r\n if (!props.validateOnlyOnBlur) {\r\n tryCommitValue(data.value);\r\n }\r\n };\r\n\r\n const handleKeyUp = (event: KeyboardEvent<HTMLInputElement>) => {\r\n event.stopPropagation();\r\n if (!props.validateOnlyOnBlur) {\r\n tryCommitValue(event.currentTarget.value);\r\n }\r\n };\r\n const handleBlur = (event: FocusEvent<HTMLInputElement>) => {\r\n HandleOnBlur(event);\r\n if (props.validateOnlyOnBlur) {\r\n tryCommitValue(event.currentTarget.value);\r\n }\r\n };\r\n\r\n const mergedClassName = mergeClasses(classes.inputFill, !validateValue(value) ? classes.invalid : \"\");\r\n\r\n const id = useId(\"input-button\");\r\n return (\r\n <div className={mergeClasses(classes.container, props.className)}>\r\n {props.infoLabel && <InfoLabel {...props.infoLabel} htmlFor={id} />}\r\n <FluentInput\r\n {...props}\r\n input={{ className: classes.inputSlot }}\r\n id={id}\r\n size={size}\r\n value={value}\r\n onChange={handleChange}\r\n onKeyUp={handleKeyUp}\r\n onKeyDown={HandleKeyDown}\r\n onBlur={handleBlur}\r\n className={mergedClassName}\r\n />\r\n </div>\r\n );\r\n};\r\n"]}
1
+ {"version":3,"file":"textInput.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/textInput.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { FunctionComponent, KeyboardEvent, ChangeEvent, FocusEvent } from \"react\";\r\nimport { useContext, useEffect, useRef, useState } from \"react\";\r\nimport type { InputOnChangeData } from \"@fluentui/react-components\";\r\nimport { Input as FluentInput, mergeClasses, useId } from \"@fluentui/react-components\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\nimport { InfoLabel } from \"./infoLabel\";\r\nimport { HandleKeyDown, HandleOnBlur, useInputStyles } from \"./utils\";\r\nimport { ToolContext } from \"../hoc/fluentToolWrapper\";\r\n\r\nexport type TextInputProps = PrimitiveProps<string> & {\r\n validator?: (value: string) => boolean;\r\n validateOnlyOnBlur?: boolean;\r\n};\r\n\r\nexport const TextInput: FunctionComponent<TextInputProps> = (props) => {\r\n TextInput.displayName = \"TextInput\";\r\n const classes = useInputStyles();\r\n const [value, setValue] = useState(props.value);\r\n const lastCommittedValue = useRef(props.value);\r\n const { size } = useContext(ToolContext);\r\n useEffect(() => {\r\n if (props.value !== lastCommittedValue.current) {\r\n setValue(props.value); // Update local state when props.value changes\r\n lastCommittedValue.current = props.value;\r\n }\r\n }, [props.value]);\r\n\r\n const validateValue = (val: string): boolean => {\r\n const failsValidator = props.validator && !props.validator(val);\r\n return !failsValidator;\r\n };\r\n\r\n const tryCommitValue = (currVal: string) => {\r\n // Only commit if valid and different from last committed value\r\n if (validateValue(currVal) && currVal !== lastCommittedValue.current) {\r\n lastCommittedValue.current = currVal;\r\n props.onChange(currVal);\r\n }\r\n };\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>, data: InputOnChangeData) => {\r\n event.stopPropagation();\r\n setValue(data.value);\r\n if (!props.validateOnlyOnBlur) {\r\n tryCommitValue(data.value);\r\n }\r\n };\r\n\r\n const handleKeyUp = (event: KeyboardEvent<HTMLInputElement>) => {\r\n event.stopPropagation();\r\n if (!props.validateOnlyOnBlur) {\r\n tryCommitValue(event.currentTarget.value);\r\n }\r\n };\r\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\r\n HandleKeyDown(event);\r\n // When validateOnlyOnBlur is set, also commit on Enter for better UX\r\n if (event.key === \"Enter\" && props.validateOnlyOnBlur) {\r\n tryCommitValue(event.currentTarget.value);\r\n }\r\n };\r\n\r\n const handleBlur = (event: FocusEvent<HTMLInputElement>) => {\r\n HandleOnBlur(event);\r\n if (props.validateOnlyOnBlur) {\r\n tryCommitValue(event.currentTarget.value);\r\n }\r\n };\r\n\r\n const mergedClassName = mergeClasses(classes.inputFill, !validateValue(value) ? classes.invalid : \"\");\r\n\r\n const id = useId(\"input-button\");\r\n return (\r\n <div className={mergeClasses(classes.container, props.className)}>\r\n {props.infoLabel && <InfoLabel {...props.infoLabel} htmlFor={id} />}\r\n <FluentInput\r\n {...props}\r\n input={{ className: classes.inputSlot }}\r\n id={id}\r\n size={size}\r\n value={value}\r\n onChange={handleChange}\r\n onKeyUp={handleKeyUp}\r\n onKeyDown={handleKeyDown}\r\n onBlur={handleBlur}\r\n className={mergedClassName}\r\n />\r\n </div>\r\n );\r\n};\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onerjs/shared-ui-components",
3
- "version": "8.42.8",
3
+ "version": "8.43.1",
4
4
  "main": "index.js",
5
5
  "module": "index.js",
6
6
  "types": "index.d.ts",