@onerjs/shared-ui-components 8.51.6 → 8.51.8

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.
@@ -18,13 +18,25 @@ function CoerceStepValue(step, isFineKeyPressed, isCourseKeyPressed) {
18
18
  }
19
19
  return step;
20
20
  }
21
- // Allow arbitrary expressions, primarily for math operations (e.g. 10*60 for 10 minutes in seconds).
22
- // Use Function constructor to safely evaluate the expression without allowing access to scope.
23
- // If the expression is invalid, fallback to NaN which will be caught by validateValue and prevent committing.
21
+ // Parse the raw input into a number, supporting plain numeric values and arbitrary math expressions
22
+ // (e.g. "10*60" for 10 minutes in seconds).
23
+ // First, try Number() so plain numeric input works even under a strict Content-Security-Policy that
24
+ // disallows eval/Function. Only fall back to the Function constructor for non-numeric inputs that may
25
+ // be expressions. Empty/whitespace input returns NaN so validateValue rejects it rather than committing
26
+ // 0 (which is what Number("") would otherwise return).
27
+ // Non-finite results (NaN, +/-Infinity) are rejected from both paths so callers don't have to handle them.
24
28
  function EvaluateExpression(rawValue) {
25
- const val = rawValue.trim();
29
+ rawValue = rawValue.trim();
30
+ if (rawValue.length === 0) {
31
+ return NaN;
32
+ }
33
+ const value = Number(rawValue);
34
+ if (Number.isFinite(value)) {
35
+ return value;
36
+ }
26
37
  try {
27
- return Number(Function(`"use strict";return (${val})`)());
38
+ const result = Number(Function(`"use strict";return (${rawValue})`)());
39
+ return Number.isFinite(result) ? result : NaN;
28
40
  }
29
41
  catch {
30
42
  return NaN;
@@ -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, type FocusEvent, type KeyboardEvent, type PointerEvent, forwardRef, useCallback, useContext, useEffect, useRef, useState } 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 { 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 // Ignore modifier keys so the useKeyState calls above can still observe them.\r\n if (event.key !== \"Alt\" && event.key !== \"Shift\") {\r\n HandleKeyDown(event);\r\n }\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"]}
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, type FocusEvent, type KeyboardEvent, type PointerEvent, forwardRef, useCallback, useContext, useEffect, useRef, useState } 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 { 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// Parse the raw input into a number, supporting plain numeric values and arbitrary math expressions\r\n// (e.g. \"10*60\" for 10 minutes in seconds).\r\n// First, try Number() so plain numeric input works even under a strict Content-Security-Policy that\r\n// disallows eval/Function. Only fall back to the Function constructor for non-numeric inputs that may\r\n// be expressions. Empty/whitespace input returns NaN so validateValue rejects it rather than committing\r\n// 0 (which is what Number(\"\") would otherwise return).\r\n// Non-finite results (NaN, +/-Infinity) are rejected from both paths so callers don't have to handle them.\r\nfunction EvaluateExpression(rawValue: string): number {\r\n rawValue = rawValue.trim();\r\n if (rawValue.length === 0) {\r\n return NaN;\r\n }\r\n\r\n const value = Number(rawValue);\r\n if (Number.isFinite(value)) {\r\n return value;\r\n }\r\n\r\n try {\r\n const result = Number(Function(`\"use strict\";return (${rawValue})`)());\r\n return Number.isFinite(result) ? result : NaN;\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 // Ignore modifier keys so the useKeyState calls above can still observe them.\r\n if (event.key !== \"Alt\" && event.key !== \"Shift\") {\r\n HandleKeyDown(event);\r\n }\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onerjs/shared-ui-components",
3
- "version": "8.51.6",
3
+ "version": "8.51.8",
4
4
  "main": "index.js",
5
5
  "module": "index.js",
6
6
  "types": "index.d.ts",