@babylonjs/shared-ui-components 8.50.0 → 8.50.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/fluent/hooks/keyboardHooks.d.ts +11 -0
  2. package/fluent/hooks/keyboardHooks.js +48 -0
  3. package/fluent/hooks/keyboardHooks.js.map +1 -0
  4. package/fluent/hooks/transientStateHooks.d.ts +7 -0
  5. package/fluent/hooks/transientStateHooks.js +20 -0
  6. package/fluent/hooks/transientStateHooks.js.map +1 -0
  7. package/fluent/icons.js +3 -3
  8. package/fluent/icons.js.map +1 -1
  9. package/fluent/primitives/accordion.js +11 -15
  10. package/fluent/primitives/accordion.js.map +1 -1
  11. package/fluent/primitives/comboBox.d.ts +23 -2
  12. package/fluent/primitives/comboBox.js +4 -4
  13. package/fluent/primitives/comboBox.js.map +1 -1
  14. package/fluent/primitives/entitySelector.js +20 -6
  15. package/fluent/primitives/entitySelector.js.map +1 -1
  16. package/fluent/primitives/infoLabel.js +4 -28
  17. package/fluent/primitives/infoLabel.js.map +1 -1
  18. package/fluent/primitives/spinButton.js +47 -4
  19. package/fluent/primitives/spinButton.js.map +1 -1
  20. package/fluent/primitives/toast.js +2 -2
  21. package/fluent/primitives/toast.js.map +1 -1
  22. package/nodeGraphSystem/graphCanvas.js +3 -0
  23. package/nodeGraphSystem/graphCanvas.js.map +1 -1
  24. package/nodeGraphSystem/graphNode.d.ts +1 -0
  25. package/nodeGraphSystem/graphNode.js +8 -0
  26. package/nodeGraphSystem/graphNode.js.map +1 -1
  27. package/nodeGraphSystem/interfaces/nodeData.d.ts +1 -0
  28. package/nodeGraphSystem/interfaces/nodeData.js.map +1 -1
  29. package/nodeGraphSystem/nodePort.d.ts +2 -0
  30. package/nodeGraphSystem/nodePort.js +11 -1
  31. package/nodeGraphSystem/nodePort.js.map +1 -1
  32. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useCallback, useState, useEffect } from "react";
3
- import { Body1Strong, InfoLabel as FluentInfoLabel, makeStyles, mergeClasses, useFluent } from "@fluentui/react-components";
2
+ import { useCallback } from "react";
3
+ import { Body1Strong, InfoLabel as FluentInfoLabel, makeStyles, mergeClasses } from "@fluentui/react-components";
4
+ import { useKeyState } from "../hooks/keyboardHooks.js";
4
5
  const useInfoLabelStyles = makeStyles({
5
6
  infoPopup: {
6
7
  whiteSpace: "normal",
@@ -27,32 +28,7 @@ const useInfoLabelStyles = makeStyles({
27
28
  export const InfoLabel = (props) => {
28
29
  InfoLabel.displayName = "InfoLabel";
29
30
  const classes = useInfoLabelStyles();
30
- const { targetDocument } = useFluent();
31
- const [ctrlPressed, setCtrlPressed] = useState(false);
32
- useEffect(() => {
33
- const targetWindow = targetDocument?.defaultView ?? window;
34
- const handleKeyDown = (e) => {
35
- if (e.ctrlKey) {
36
- setCtrlPressed(true);
37
- }
38
- };
39
- const handleKeyUp = (e) => {
40
- if (!e.ctrlKey) {
41
- setCtrlPressed(false);
42
- }
43
- };
44
- const handleBlur = () => {
45
- setCtrlPressed(false);
46
- };
47
- targetWindow.addEventListener("keydown", handleKeyDown);
48
- targetWindow.addEventListener("keyup", handleKeyUp);
49
- targetWindow.addEventListener("blur", handleBlur);
50
- return () => {
51
- targetWindow.removeEventListener("keydown", handleKeyDown);
52
- targetWindow.removeEventListener("keyup", handleKeyUp);
53
- targetWindow.removeEventListener("blur", handleBlur);
54
- };
55
- }, [targetDocument]);
31
+ const ctrlPressed = useKeyState("Control");
56
32
  const infoContent = props.info ? _jsx("div", { className: classes.infoPopup, children: props.info }) : undefined;
57
33
  const showCopyCursor = ctrlPressed && props.onContextMenu;
58
34
  // Handle Ctrl+click as context menu action
@@ -1 +1 @@
1
- {"version":3,"file":"infoLabel.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/infoLabel.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { FunctionComponent, MouseEvent, MouseEventHandler } from \"react\";\r\nimport { useCallback, useState, useEffect } from \"react\";\r\nimport { Body1Strong, InfoLabel as FluentInfoLabel, makeStyles, mergeClasses, useFluent } from \"@fluentui/react-components\";\r\n\r\nexport type InfoLabelProps = {\r\n htmlFor: string; // required ID of the element whose label we are applying\r\n info?: JSX.Element;\r\n label: string;\r\n className?: string;\r\n /**\r\n * When true, applies flex layout styling to the label slot for proper truncation in flex containers\r\n */\r\n flexLabel?: boolean;\r\n /**\r\n * Handler for right-click context menu. Also triggers on Ctrl+click.\r\n */\r\n onContextMenu?: MouseEventHandler;\r\n};\r\nexport type InfoLabelParentProps = Omit<InfoLabelProps, \"htmlFor\">;\r\nconst useInfoLabelStyles = makeStyles({\r\n infoPopup: {\r\n whiteSpace: \"normal\",\r\n wordBreak: \"break-word\",\r\n },\r\n labelSlot: {\r\n display: \"flex\",\r\n minWidth: 0,\r\n },\r\n labelText: {\r\n whiteSpace: \"nowrap\",\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n },\r\n copyable: {\r\n cursor: \"pointer\",\r\n },\r\n});\r\n/**\r\n * Renders a label with an optional popup containing more info\r\n * @param props\r\n * @returns\r\n */\r\nexport const InfoLabel: FunctionComponent<InfoLabelProps> = (props) => {\r\n InfoLabel.displayName = \"InfoLabel\";\r\n const classes = useInfoLabelStyles();\r\n const { targetDocument } = useFluent();\r\n\r\n const [ctrlPressed, setCtrlPressed] = useState(false);\r\n\r\n useEffect(() => {\r\n const targetWindow = targetDocument?.defaultView ?? window;\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.ctrlKey) {\r\n setCtrlPressed(true);\r\n }\r\n };\r\n const handleKeyUp = (e: KeyboardEvent) => {\r\n if (!e.ctrlKey) {\r\n setCtrlPressed(false);\r\n }\r\n };\r\n const handleBlur = () => {\r\n setCtrlPressed(false);\r\n };\r\n\r\n targetWindow.addEventListener(\"keydown\", handleKeyDown);\r\n targetWindow.addEventListener(\"keyup\", handleKeyUp);\r\n targetWindow.addEventListener(\"blur\", handleBlur);\r\n\r\n return () => {\r\n targetWindow.removeEventListener(\"keydown\", handleKeyDown);\r\n targetWindow.removeEventListener(\"keyup\", handleKeyUp);\r\n targetWindow.removeEventListener(\"blur\", handleBlur);\r\n };\r\n }, [targetDocument]);\r\n\r\n const infoContent = props.info ? <div className={classes.infoPopup}>{props.info}</div> : undefined;\r\n\r\n const showCopyCursor = ctrlPressed && props.onContextMenu;\r\n\r\n // Handle Ctrl+click as context menu action\r\n const handleClick = useCallback(\r\n (e: MouseEvent) => {\r\n if (e.ctrlKey && props.onContextMenu) {\r\n props.onContextMenu(e);\r\n }\r\n },\r\n [props.onContextMenu]\r\n );\r\n\r\n return infoContent ? (\r\n <FluentInfoLabel\r\n htmlFor={props.htmlFor}\r\n info={infoContent}\r\n className={mergeClasses(props.className, showCopyCursor ? classes.copyable : undefined)}\r\n label={props.flexLabel ? { className: classes.labelSlot } : undefined}\r\n onContextMenu={props.onContextMenu}\r\n onClick={handleClick}\r\n >\r\n <Body1Strong className={classes.labelText}>{props.label}</Body1Strong>\r\n </FluentInfoLabel>\r\n ) : (\r\n <Body1Strong className={mergeClasses(props.className, showCopyCursor ? classes.copyable : undefined)} onContextMenu={props.onContextMenu} onClick={handleClick}>\r\n {props.label}\r\n </Body1Strong>\r\n );\r\n};\r\n"]}
1
+ {"version":3,"file":"infoLabel.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/infoLabel.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,SAAS,IAAI,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AACjH,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAiBrD,MAAM,kBAAkB,GAAG,UAAU,CAAC;IAClC,SAAS,EAAE;QACP,UAAU,EAAE,QAAQ;QACpB,SAAS,EAAE,YAAY;KAC1B;IACD,SAAS,EAAE;QACP,OAAO,EAAE,MAAM;QACf,QAAQ,EAAE,CAAC;KACd;IACD,SAAS,EAAE;QACP,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,QAAQ;QAClB,YAAY,EAAE,UAAU;KAC3B;IACD,QAAQ,EAAE;QACN,MAAM,EAAE,SAAS;KACpB;CACJ,CAAC,CAAC;AACH;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAsC,CAAC,KAAK,EAAE,EAAE;IAClE,SAAS,CAAC,WAAW,GAAG,WAAW,CAAC;IACpC,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;IAErC,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAE3C,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,cAAK,SAAS,EAAE,OAAO,CAAC,SAAS,YAAG,KAAK,CAAC,IAAI,GAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnG,MAAM,cAAc,GAAG,WAAW,IAAI,KAAK,CAAC,aAAa,CAAC;IAE1D,2CAA2C;IAC3C,MAAM,WAAW,GAAG,WAAW,CAC3B,CAAC,CAAa,EAAE,EAAE;QACd,IAAI,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;YACnC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC;IACL,CAAC,EACD,CAAC,KAAK,CAAC,aAAa,CAAC,CACxB,CAAC;IAEF,OAAO,WAAW,CAAC,CAAC,CAAC,CACjB,KAAC,eAAe,IACZ,OAAO,EAAE,KAAK,CAAC,OAAO,EACtB,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,YAAY,CAAC,KAAK,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,EACvF,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,EACrE,aAAa,EAAE,KAAK,CAAC,aAAa,EAClC,OAAO,EAAE,WAAW,YAEpB,KAAC,WAAW,IAAC,SAAS,EAAE,OAAO,CAAC,SAAS,YAAG,KAAK,CAAC,KAAK,GAAe,GACxD,CACrB,CAAC,CAAC,CAAC,CACA,KAAC,WAAW,IAAC,SAAS,EAAE,YAAY,CAAC,KAAK,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,OAAO,EAAE,WAAW,YACzJ,KAAK,CAAC,KAAK,GACF,CACjB,CAAC;AACN,CAAC,CAAC","sourcesContent":["import type { FunctionComponent, MouseEvent, MouseEventHandler } from \"react\";\r\nimport { useCallback } from \"react\";\r\nimport { Body1Strong, InfoLabel as FluentInfoLabel, makeStyles, mergeClasses } from \"@fluentui/react-components\";\r\nimport { useKeyState } from \"../hooks/keyboardHooks\";\r\n\r\nexport type InfoLabelProps = {\r\n htmlFor: string; // required ID of the element whose label we are applying\r\n info?: JSX.Element;\r\n label: string;\r\n className?: string;\r\n /**\r\n * When true, applies flex layout styling to the label slot for proper truncation in flex containers\r\n */\r\n flexLabel?: boolean;\r\n /**\r\n * Handler for right-click context menu. Also triggers on Ctrl+click.\r\n */\r\n onContextMenu?: MouseEventHandler;\r\n};\r\nexport type InfoLabelParentProps = Omit<InfoLabelProps, \"htmlFor\">;\r\nconst useInfoLabelStyles = makeStyles({\r\n infoPopup: {\r\n whiteSpace: \"normal\",\r\n wordBreak: \"break-word\",\r\n },\r\n labelSlot: {\r\n display: \"flex\",\r\n minWidth: 0,\r\n },\r\n labelText: {\r\n whiteSpace: \"nowrap\",\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n },\r\n copyable: {\r\n cursor: \"pointer\",\r\n },\r\n});\r\n/**\r\n * Renders a label with an optional popup containing more info\r\n * @param props\r\n * @returns\r\n */\r\nexport const InfoLabel: FunctionComponent<InfoLabelProps> = (props) => {\r\n InfoLabel.displayName = \"InfoLabel\";\r\n const classes = useInfoLabelStyles();\r\n\r\n const ctrlPressed = useKeyState(\"Control\");\r\n\r\n const infoContent = props.info ? <div className={classes.infoPopup}>{props.info}</div> : undefined;\r\n\r\n const showCopyCursor = ctrlPressed && props.onContextMenu;\r\n\r\n // Handle Ctrl+click as context menu action\r\n const handleClick = useCallback(\r\n (e: MouseEvent) => {\r\n if (e.ctrlKey && props.onContextMenu) {\r\n props.onContextMenu(e);\r\n }\r\n },\r\n [props.onContextMenu]\r\n );\r\n\r\n return infoContent ? (\r\n <FluentInfoLabel\r\n htmlFor={props.htmlFor}\r\n info={infoContent}\r\n className={mergeClasses(props.className, showCopyCursor ? classes.copyable : undefined)}\r\n label={props.flexLabel ? { className: classes.labelSlot } : undefined}\r\n onContextMenu={props.onContextMenu}\r\n onClick={handleClick}\r\n >\r\n <Body1Strong className={classes.labelText}>{props.label}</Body1Strong>\r\n </FluentInfoLabel>\r\n ) : (\r\n <Body1Strong className={mergeClasses(props.className, showCopyCursor ? classes.copyable : undefined)} onContextMenu={props.onContextMenu} onClick={handleClick}>\r\n {props.label}\r\n </Body1Strong>\r\n );\r\n};\r\n"]}
@@ -4,6 +4,18 @@ import { forwardRef, useEffect, useState, useRef, useContext } from "react";
4
4
  import { InfoLabel } from "./infoLabel.js";
5
5
  import { CalculatePrecision, HandleKeyDown, HandleOnBlur, useInputStyles } from "./utils.js";
6
6
  import { ToolContext } from "../hoc/fluentToolWrapper.js";
7
+ import { useKeyState } from "../hooks/keyboardHooks.js";
8
+ function CoerceStepValue(step, isAltKeyPressed, isShiftKeyPressed) {
9
+ // When the alt key is pressed, decrease step by a factor of 10.
10
+ if (isAltKeyPressed) {
11
+ return step * 0.1;
12
+ }
13
+ // When the shift key is pressed, increase step by a factor of 10.
14
+ if (isShiftKeyPressed) {
15
+ return step * 10;
16
+ }
17
+ return step;
18
+ }
7
19
  export const SpinButton = forwardRef((props, ref) => {
8
20
  SpinButton.displayName = "SpinButton";
9
21
  const classes = useInputStyles();
@@ -11,9 +23,15 @@ export const SpinButton = forwardRef((props, ref) => {
11
23
  const { min, max } = props;
12
24
  const [value, setValue] = useState(props.value);
13
25
  const lastCommittedValue = useRef(props.value);
26
+ // When the input does not have keyboard focus
27
+ const isUnfocusedAltKeyPressed = useKeyState("Alt");
28
+ const isUnfocusedShiftKeyPressed = useKeyState("Shift");
29
+ // When the input does have keyboard focus
30
+ const [isFocusedAltKeyPressed, setIsFocusedAltKeyPressed] = useState(false);
31
+ const [isFocusedShiftKeyPressed, setIsFocusedShiftKeyPressed] = useState(false);
14
32
  // step and forceInt are not mutually exclusive since there could be cases where you want to forceInt but have spinButton jump >1 int per spin
15
- const step = props.step != undefined ? props.step : props.forceInt ? 1 : undefined;
16
- const precision = Math.min(4, step !== undefined ? Math.max(0, CalculatePrecision(step)) : 2); // If no step, set precision to 2. Regardless, cap precision at 4 to avoid wild numbers
33
+ const step = CoerceStepValue(props.step ?? 1, isUnfocusedAltKeyPressed || isFocusedAltKeyPressed, isUnfocusedShiftKeyPressed || isFocusedShiftKeyPressed);
34
+ const precision = Math.min(4, Math.max(0, CalculatePrecision(step))); // Cap precision at 4 to avoid wild numbers
17
35
  useEffect(() => {
18
36
  if (props.value !== lastCommittedValue.current) {
19
37
  lastCommittedValue.current = props.value;
@@ -41,10 +59,35 @@ export const SpinButton = forwardRef((props, ref) => {
41
59
  tryCommitValue(data.value);
42
60
  }
43
61
  };
62
+ const handleKeyDown = (event) => {
63
+ if (event.key === "Alt") {
64
+ setIsFocusedAltKeyPressed(true);
65
+ }
66
+ else if (event.key === "Shift") {
67
+ setIsFocusedShiftKeyPressed(true);
68
+ }
69
+ HandleKeyDown(event);
70
+ };
44
71
  const handleKeyUp = (event) => {
45
72
  event.stopPropagation(); // Prevent event propagation
46
73
  if (event.key !== "Enter") {
47
- const currVal = parseFloat(event.target.value); // Cannot use currentTarget.value as it won't have the most recently typed value
74
+ if (event.key === "Alt") {
75
+ setIsFocusedAltKeyPressed(false);
76
+ }
77
+ else if (event.key === "Shift") {
78
+ setIsFocusedShiftKeyPressed(false);
79
+ }
80
+ // Allow arbitrary expressions, primarily for math operations (e.g. 10*60 for 10 minutes in seconds).
81
+ // Use Function constructor to safely evaluate the expression without allowing access to scope.
82
+ // If the expression is invalid, fallback to NaN which will be caught by validateValue and prevent committing.
83
+ const currVal = ((val) => {
84
+ try {
85
+ return Number(Function(`"use strict";return (${val})`)());
86
+ }
87
+ catch {
88
+ return NaN;
89
+ }
90
+ })(event.target.value);
48
91
  setValue(currVal);
49
92
  tryCommitValue(currVal);
50
93
  }
@@ -55,7 +98,7 @@ export const SpinButton = forwardRef((props, ref) => {
55
98
  const inputSlot = {
56
99
  className: mergeClasses(classes.inputSlot, props.inputClassName),
57
100
  };
58
- const spinButton = (_jsx(FluentSpinButton, { ref: ref, ...props, appearance: "outline", input: inputSlot, step: step, id: id, size: size, precision: precision, displayValue: `${value.toFixed(precision)}${props.unit ? " " + props.unit : ""}`, value: value, onChange: handleChange, onKeyUp: handleKeyUp, onKeyDown: HandleKeyDown, onBlur: HandleOnBlur, className: mergedClassName }));
101
+ const spinButton = (_jsx(FluentSpinButton, { ref: ref, ...props, appearance: "outline", input: inputSlot, step: step, id: id, size: size, precision: precision, displayValue: `${value.toFixed(precision)}${props.unit ? " " + props.unit : ""}`, value: value, onChange: handleChange, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, onBlur: HandleOnBlur, className: mergedClassName }));
59
102
  return props.infoLabel ? (_jsxs("div", { className: classes.container, children: [_jsx(InfoLabel, { ...props.infoLabel, htmlFor: id }), spinButton] })) : (spinButton);
60
103
  });
61
104
  //# sourceMappingURL=spinButton.js.map
@@ -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 { SpinButton as FluentSpinButton, mergeClasses, useId } from \"@fluentui/react-components\";\r\nimport type { SpinButtonOnChangeData, SpinButtonChangeEvent } from \"@fluentui/react-components\";\r\nimport type { KeyboardEvent } from \"react\";\r\nimport { forwardRef, useEffect, useState, useRef, useContext } from \"react\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\nimport { InfoLabel } from \"./infoLabel\";\r\nimport { CalculatePrecision, HandleKeyDown, HandleOnBlur, useInputStyles } from \"./utils\";\r\nimport { ToolContext } from \"../hoc/fluentToolWrapper\";\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 className for the input element */\r\n inputClassName?: string;\r\n};\r\n\r\nexport const SpinButton = forwardRef<HTMLInputElement, SpinButtonProps>((props, ref) => {\r\n SpinButton.displayName = \"SpinButton\";\r\n const classes = useInputStyles();\r\n const { size } = useContext(ToolContext);\r\n\r\n const { min, max } = props;\r\n\r\n const [value, setValue] = useState(props.value);\r\n const lastCommittedValue = useRef(props.value);\r\n // step and forceInt are not mutually exclusive since there could be cases where you want to forceInt but have spinButton jump >1 int per spin\r\n const step = props.step != undefined ? props.step : props.forceInt ? 1 : undefined;\r\n const precision = Math.min(4, step !== undefined ? Math.max(0, CalculatePrecision(step)) : 2); // If no step, set precision to 2. Regardless, cap precision at 4 to avoid wild numbers\r\n\r\n useEffect(() => {\r\n if (props.value !== lastCommittedValue.current) {\r\n lastCommittedValue.current = props.value;\r\n setValue(props.value); // Update local state when props.value changes\r\n }\r\n }, [props.value]);\r\n\r\n const validateValue = (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\r\n const tryCommitValue = (currVal: number) => {\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: SpinButtonChangeEvent, data: SpinButtonOnChangeData) => {\r\n event.stopPropagation(); // Prevent event propagation\r\n if (data.value != null && !Number.isNaN(data.value)) {\r\n setValue(data.value);\r\n tryCommitValue(data.value);\r\n }\r\n };\r\n\r\n const handleKeyUp = (event: KeyboardEvent<HTMLInputElement>) => {\r\n event.stopPropagation(); // Prevent event propagation\r\n\r\n if (event.key !== \"Enter\") {\r\n const currVal = parseFloat((event.target as any).value); // Cannot use currentTarget.value as it won't have the most recently typed value\r\n setValue(currVal);\r\n tryCommitValue(currVal);\r\n }\r\n };\r\n\r\n const id = useId(\"spin-button\");\r\n const mergedClassName = mergeClasses(classes.input, !validateValue(value) ? classes.invalid : \"\", props.className);\r\n\r\n // Build input slot from inputClassName\r\n const inputSlot = {\r\n className: mergeClasses(classes.inputSlot, props.inputClassName),\r\n };\r\n\r\n const spinButton = (\r\n <FluentSpinButton\r\n ref={ref}\r\n {...props}\r\n appearance=\"outline\"\r\n input={inputSlot}\r\n step={step}\r\n id={id}\r\n size={size}\r\n precision={precision}\r\n displayValue={`${value.toFixed(precision)}${props.unit ? \" \" + props.unit : \"\"}`}\r\n value={value}\r\n onChange={handleChange}\r\n onKeyUp={handleKeyUp}\r\n onKeyDown={HandleKeyDown}\r\n onBlur={HandleOnBlur}\r\n className={mergedClassName}\r\n />\r\n );\r\n\r\n return props.infoLabel ? (\r\n <div className={classes.container}>\r\n <InfoLabel {...props.infoLabel} htmlFor={id} />\r\n {spinButton}\r\n </div>\r\n ) : (\r\n spinButton\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 { SpinButton as FluentSpinButton, mergeClasses, useId } from \"@fluentui/react-components\";\r\nimport type { SpinButtonOnChangeData, SpinButtonChangeEvent } from \"@fluentui/react-components\";\r\nimport type { KeyboardEvent } from \"react\";\r\nimport { forwardRef, useEffect, useState, useRef, useContext } from \"react\";\r\nimport type { PrimitiveProps } from \"./primitive\";\r\nimport { InfoLabel } from \"./infoLabel\";\r\nimport { CalculatePrecision, HandleKeyDown, HandleOnBlur, useInputStyles } from \"./utils\";\r\nimport { ToolContext } from \"../hoc/fluentToolWrapper\";\r\nimport { useKeyState } from \"../hooks/keyboardHooks\";\r\n\r\nfunction CoerceStepValue(step: number, isAltKeyPressed: boolean, isShiftKeyPressed: boolean): number {\r\n // When the alt key is pressed, decrease step by a factor of 10.\r\n if (isAltKeyPressed) {\r\n return step * 0.1;\r\n }\r\n\r\n // When the shift key is pressed, increase step by a factor of 10.\r\n if (isShiftKeyPressed) {\r\n return step * 10;\r\n }\r\n\r\n return step;\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 className for the input element */\r\n inputClassName?: string;\r\n};\r\n\r\nexport const SpinButton = forwardRef<HTMLInputElement, SpinButtonProps>((props, ref) => {\r\n SpinButton.displayName = \"SpinButton\";\r\n const classes = useInputStyles();\r\n const { size } = useContext(ToolContext);\r\n\r\n const { min, max } = props;\r\n\r\n const [value, setValue] = useState(props.value);\r\n const lastCommittedValue = useRef(props.value);\r\n\r\n // When the input does not have keyboard focus\r\n const isUnfocusedAltKeyPressed = useKeyState(\"Alt\");\r\n const isUnfocusedShiftKeyPressed = useKeyState(\"Shift\");\r\n\r\n // When the input does have keyboard focus\r\n const [isFocusedAltKeyPressed, setIsFocusedAltKeyPressed] = useState(false);\r\n const [isFocusedShiftKeyPressed, setIsFocusedShiftKeyPressed] = useState(false);\r\n\r\n // step and forceInt are not mutually exclusive since there could be cases where you want to forceInt but have spinButton jump >1 int per spin\r\n const step = CoerceStepValue(props.step ?? 1, isUnfocusedAltKeyPressed || isFocusedAltKeyPressed, isUnfocusedShiftKeyPressed || isFocusedShiftKeyPressed);\r\n const precision = Math.min(4, Math.max(0, CalculatePrecision(step))); // Cap precision at 4 to avoid wild numbers\r\n\r\n useEffect(() => {\r\n if (props.value !== lastCommittedValue.current) {\r\n lastCommittedValue.current = props.value;\r\n setValue(props.value); // Update local state when props.value changes\r\n }\r\n }, [props.value]);\r\n\r\n const validateValue = (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\r\n const tryCommitValue = (currVal: number) => {\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: SpinButtonChangeEvent, data: SpinButtonOnChangeData) => {\r\n event.stopPropagation(); // Prevent event propagation\r\n if (data.value != null && !Number.isNaN(data.value)) {\r\n setValue(data.value);\r\n tryCommitValue(data.value);\r\n }\r\n };\r\n\r\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\r\n if (event.key === \"Alt\") {\r\n setIsFocusedAltKeyPressed(true);\r\n } else if (event.key === \"Shift\") {\r\n setIsFocusedShiftKeyPressed(true);\r\n }\r\n\r\n HandleKeyDown(event);\r\n };\r\n\r\n const handleKeyUp = (event: KeyboardEvent<HTMLInputElement>) => {\r\n event.stopPropagation(); // Prevent event propagation\r\n\r\n if (event.key !== \"Enter\") {\r\n if (event.key === \"Alt\") {\r\n setIsFocusedAltKeyPressed(false);\r\n } else if (event.key === \"Shift\") {\r\n setIsFocusedShiftKeyPressed(false);\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\n const currVal = ((val: string): number => {\r\n try {\r\n return Number(Function(`\"use strict\";return (${val})`)());\r\n } catch {\r\n return NaN;\r\n }\r\n })((event.target as any).value);\r\n\r\n setValue(currVal);\r\n tryCommitValue(currVal);\r\n }\r\n };\r\n\r\n const id = useId(\"spin-button\");\r\n const mergedClassName = mergeClasses(classes.input, !validateValue(value) ? classes.invalid : \"\", props.className);\r\n\r\n // Build input slot from inputClassName\r\n const inputSlot = {\r\n className: mergeClasses(classes.inputSlot, props.inputClassName),\r\n };\r\n\r\n const spinButton = (\r\n <FluentSpinButton\r\n ref={ref}\r\n {...props}\r\n appearance=\"outline\"\r\n input={inputSlot}\r\n step={step}\r\n id={id}\r\n size={size}\r\n precision={precision}\r\n displayValue={`${value.toFixed(precision)}${props.unit ? \" \" + props.unit : \"\"}`}\r\n value={value}\r\n onChange={handleChange}\r\n onKeyDown={handleKeyDown}\r\n onKeyUp={handleKeyUp}\r\n onBlur={HandleOnBlur}\r\n className={mergedClassName}\r\n />\r\n );\r\n\r\n return props.infoLabel ? (\r\n <div className={classes.container}>\r\n <InfoLabel {...props.infoLabel} htmlFor={id} />\r\n {spinButton}\r\n </div>\r\n ) : (\r\n spinButton\r\n );\r\n});\r\n"]}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { FluentProvider, Toast, ToastBody, Toaster, useId, useToastController, useFluent } from "@fluentui/react-components";
2
+ import { FluentProvider, Toast, Toaster, ToastTitle, useFluent, useId, useToastController } from "@fluentui/react-components";
3
3
  import { createContext, useCallback, useContext } from "react";
4
4
  const ToastContext = createContext({ showToast: () => { } });
5
5
  export const ToastProvider = ({ children }) => {
@@ -7,7 +7,7 @@ export const ToastProvider = ({ children }) => {
7
7
  const { dispatchToast } = useToastController(toasterId);
8
8
  const { targetDocument } = useFluent();
9
9
  const showToast = useCallback((message) => {
10
- dispatchToast(_jsx(Toast, { children: _jsx(ToastBody, { children: message }) }), { intent: "success", timeout: 1000 });
10
+ dispatchToast(_jsx(Toast, { children: _jsx(ToastTitle, { children: message }) }), { intent: "info", timeout: 2000 });
11
11
  }, [dispatchToast]);
12
12
  return (_jsxs(ToastContext.Provider, { value: { showToast }, children: [children, _jsx(FluentProvider, { applyStylesToPortals: true, targetDocument: targetDocument, children: _jsx(Toaster, { toasterId: toasterId, position: "bottom-end" }) })] }));
13
13
  };
@@ -1 +1 @@
1
- {"version":3,"file":"toast.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/toast.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE7H,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAM/D,MAAM,YAAY,GAAG,aAAa,CAAmB,EAAE,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;AAE9E,MAAM,CAAC,MAAM,aAAa,GAAyC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;IAChF,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;IACnC,MAAM,EAAE,aAAa,EAAE,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACxD,MAAM,EAAE,cAAc,EAAE,GAAG,SAAS,EAAE,CAAC;IAEvC,MAAM,SAAS,GAAG,WAAW,CACzB,CAAC,OAAe,EAAE,EAAE;QAChB,aAAa,CACT,KAAC,KAAK,cACF,KAAC,SAAS,cAAE,OAAO,GAAa,GAC5B,EACR,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,CACvC,CAAC;IACN,CAAC,EACD,CAAC,aAAa,CAAC,CAClB,CAAC;IAEF,OAAO,CACH,MAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,SAAS,EAAE,aACtC,QAAQ,EACT,KAAC,cAAc,IAAC,oBAAoB,QAAC,cAAc,EAAE,cAAc,YAC/D,KAAC,OAAO,IAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAC,YAAY,GAAG,GAC1C,IACG,CAC3B,CAAC;AACN,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,QAAQ;IACpB,OAAO,UAAU,CAAC,YAAY,CAAC,CAAC;AACpC,CAAC","sourcesContent":["import { FluentProvider, Toast, ToastBody, Toaster, useId, useToastController, useFluent } from \"@fluentui/react-components\";\r\nimport type { FunctionComponent, PropsWithChildren } from \"react\";\r\nimport { createContext, useCallback, useContext } from \"react\";\r\n\r\ntype ToastContextType = {\r\n showToast: (message: string) => void;\r\n};\r\n\r\nconst ToastContext = createContext<ToastContextType>({ showToast: () => {} });\r\n\r\nexport const ToastProvider: FunctionComponent<PropsWithChildren> = ({ children }) => {\r\n const toasterId = useId(\"toaster\");\r\n const { dispatchToast } = useToastController(toasterId);\r\n const { targetDocument } = useFluent();\r\n\r\n const showToast = useCallback(\r\n (message: string) => {\r\n dispatchToast(\r\n <Toast>\r\n <ToastBody>{message}</ToastBody>\r\n </Toast>,\r\n { intent: \"success\", timeout: 1000 }\r\n );\r\n },\r\n [dispatchToast]\r\n );\r\n\r\n return (\r\n <ToastContext.Provider value={{ showToast }}>\r\n {children}\r\n <FluentProvider applyStylesToPortals targetDocument={targetDocument}>\r\n <Toaster toasterId={toasterId} position=\"bottom-end\" />\r\n </FluentProvider>\r\n </ToastContext.Provider>\r\n );\r\n};\r\n\r\n/**\r\n * Hook to show toast notifications.\r\n * @returns Object with showToast function that accepts a message string\r\n */\r\nexport function useToast() {\r\n return useContext(ToastContext);\r\n}\r\n"]}
1
+ {"version":3,"file":"toast.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/toast.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAC9H,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAM/D,MAAM,YAAY,GAAG,aAAa,CAAmB,EAAE,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;AAE9E,MAAM,CAAC,MAAM,aAAa,GAAyC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;IAChF,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;IACnC,MAAM,EAAE,aAAa,EAAE,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACxD,MAAM,EAAE,cAAc,EAAE,GAAG,SAAS,EAAE,CAAC;IAEvC,MAAM,SAAS,GAAG,WAAW,CACzB,CAAC,OAAe,EAAE,EAAE;QAChB,aAAa,CACT,KAAC,KAAK,cACF,KAAC,UAAU,cAAE,OAAO,GAAc,GAC9B,EACR,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CACpC,CAAC;IACN,CAAC,EACD,CAAC,aAAa,CAAC,CAClB,CAAC;IAEF,OAAO,CACH,MAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,SAAS,EAAE,aACtC,QAAQ,EACT,KAAC,cAAc,IAAC,oBAAoB,QAAC,cAAc,EAAE,cAAc,YAC/D,KAAC,OAAO,IAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAC,YAAY,GAAG,GAC1C,IACG,CAC3B,CAAC;AACN,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,QAAQ;IACpB,OAAO,UAAU,CAAC,YAAY,CAAC,CAAC;AACpC,CAAC","sourcesContent":["import type { FunctionComponent, PropsWithChildren } from \"react\";\r\n\r\nimport { FluentProvider, Toast, Toaster, ToastTitle, useFluent, useId, useToastController } from \"@fluentui/react-components\";\r\nimport { createContext, useCallback, useContext } from \"react\";\r\n\r\ntype ToastContextType = {\r\n showToast: (message: string) => void;\r\n};\r\n\r\nconst ToastContext = createContext<ToastContextType>({ showToast: () => {} });\r\n\r\nexport const ToastProvider: FunctionComponent<PropsWithChildren> = ({ children }) => {\r\n const toasterId = useId(\"toaster\");\r\n const { dispatchToast } = useToastController(toasterId);\r\n const { targetDocument } = useFluent();\r\n\r\n const showToast = useCallback(\r\n (message: string) => {\r\n dispatchToast(\r\n <Toast>\r\n <ToastTitle>{message}</ToastTitle>\r\n </Toast>,\r\n { intent: \"info\", timeout: 2000 }\r\n );\r\n },\r\n [dispatchToast]\r\n );\r\n\r\n return (\r\n <ToastContext.Provider value={{ showToast }}>\r\n {children}\r\n <FluentProvider applyStylesToPortals targetDocument={targetDocument}>\r\n <Toaster toasterId={toasterId} position=\"bottom-end\" />\r\n </FluentProvider>\r\n </ToastContext.Provider>\r\n );\r\n};\r\n\r\n/**\r\n * Hook to show toast notifications.\r\n * @returns Object with showToast function that accepts a message string\r\n */\r\nexport function useToast() {\r\n return useContext(ToastContext);\r\n}\r\n"]}
@@ -337,6 +337,9 @@ export class GraphCanvasComponent extends React.Component {
337
337
  if (this.selectedLink) {
338
338
  needRebuild = true;
339
339
  this.selectedLink.dispose();
340
+ if (this.selectedLink.nodeB) {
341
+ RefreshNode(this.selectedLink.nodeB);
342
+ }
340
343
  }
341
344
  if (this.selectedFrames.length) {
342
345
  needRebuild = true;