@react-aria/textfield
Advanced tools
Comparing version 3.0.0-nightly.992 to 3.0.0-nightly.994
141
dist/main.js
var { | ||
useEffect, | ||
useRef | ||
} = require("react"); | ||
var { | ||
useLabel | ||
@@ -87,2 +92,138 @@ } = require("@react-aria/label"); | ||
exports.useTextField = useTextField; | ||
function $e12a6959a5c62c5783c0140bc17c4eda$var$supportsNativeBeforeInputEvent() { | ||
return typeof window !== 'undefined' && window.InputEvent && // @ts-ignore | ||
typeof InputEvent.prototype.getTargetRanges === 'function'; | ||
} | ||
function useFormattedTextField(props, state, inputRef) { | ||
let stateRef = useRef(state); | ||
stateRef.current = state; // All browsers implement the 'beforeinput' event natively except Firefox | ||
// (currently behind a flag as of Firefox 84). React's polyfill does not | ||
// run in all cases that the native event fires, e.g. when deleting text. | ||
// Use the native event if available so that we can prevent invalid deletions. | ||
// We do not attempt to polyfill this in Firefox since it would be very complicated, | ||
// the benefit of doing so is fairly minor, and it's going to be natively supported soon. | ||
useEffect(() => { | ||
if (!$e12a6959a5c62c5783c0140bc17c4eda$var$supportsNativeBeforeInputEvent()) { | ||
return; | ||
} | ||
let input = inputRef.current; | ||
let onBeforeInput = e => { | ||
let state = stateRef.current; // Compute the next value of the input if the event is allowed to proceed. | ||
// See https://www.w3.org/TR/input-events-2/#interface-InputEvent-Attributes for a full list of input types. | ||
let nextValue; | ||
switch (e.inputType) { | ||
case 'historyUndo': | ||
case 'historyRedo': | ||
// Explicitly allow undo/redo. e.data is null in this case, but there's no need to validate, | ||
// because presumably the input would have already been validated previously. | ||
return; | ||
case 'deleteContent': | ||
case 'deleteByCut': | ||
case 'deleteByDrag': | ||
nextValue = input.value.slice(0, input.selectionStart) + input.value.slice(input.selectionEnd); | ||
break; | ||
case 'deleteContentForward': | ||
// This is potentially incorrect, since the browser may actually delete more than a single UTF-16 | ||
// character. In reality, a full Unicode grapheme cluster consisting of multiple UTF-16 characters | ||
// or code points may be deleted. However, in our currently supported locales, there are no such cases. | ||
// If we support additional locales in the future, this may need to change. | ||
nextValue = input.selectionEnd === input.selectionStart ? input.value.slice(0, input.selectionStart) + input.value.slice(input.selectionEnd + 1) : input.value.slice(0, input.selectionStart) + input.value.slice(input.selectionEnd); | ||
break; | ||
case 'deleteContentBackward': | ||
nextValue = input.selectionEnd === input.selectionStart ? input.value.slice(0, input.selectionStart - 1) + input.value.slice(input.selectionStart) : input.value.slice(0, input.selectionStart) + input.value.slice(input.selectionEnd); | ||
break; | ||
default: | ||
if (e.data != null) { | ||
nextValue = input.value.slice(0, input.selectionStart) + e.data + input.value.slice(input.selectionEnd); | ||
} | ||
break; | ||
} // If we did not compute a value, or the new value is invalid, prevent the event | ||
// so that the browser does not update the input text, move the selection, or add to | ||
// the undo/redo stack. | ||
if (nextValue == null || !state.validate(nextValue)) { | ||
e.preventDefault(); | ||
} | ||
}; | ||
input.addEventListener('beforeinput', onBeforeInput, false); | ||
return () => { | ||
input.removeEventListener('beforeinput', onBeforeInput, false); | ||
}; | ||
}, [inputRef, stateRef]); | ||
let onBeforeInput = !$e12a6959a5c62c5783c0140bc17c4eda$var$supportsNativeBeforeInputEvent() ? e => { | ||
let nextValue = e.target.value.slice(0, e.target.selectionStart) + e.data + e.target.value.slice(e.target.selectionEnd); | ||
if (!state.validate(nextValue)) { | ||
e.preventDefault(); | ||
} | ||
} : null; | ||
let { | ||
labelProps, | ||
inputProps: textFieldProps | ||
} = useTextField(props, inputRef); | ||
let compositionStartState = useRef(null); | ||
return { | ||
inputProps: mergeProps(textFieldProps, { | ||
onBeforeInput, | ||
onCompositionStart() { | ||
// Chrome does not implement Input Events Level 2, which specifies the insertFromComposition | ||
// and deleteByComposition inputType values for the beforeinput event. These are meant to occur | ||
// at the end of a composition (e.g. Pinyin IME, Android auto correct, etc.), and crucially, are | ||
// cancelable. The insertCompositionText and deleteCompositionText input types are not cancelable, | ||
// nor would we want to cancel them because the input from the user is incomplete at that point. | ||
// In Safari, insertFromComposition/deleteFromComposition will fire, however, allowing us to cancel | ||
// the final composition result if it is invalid. As a fallback for Chrome and Firefox, which either | ||
// don't support Input Events Level 2, or beforeinput at all, we store the state of the input when | ||
// the compositionstart event fires, and undo the changes in compositionend (below) if it is invalid. | ||
// Unfortunately, this messes up the undo/redo stack, but until insertFromComposition/deleteByComposition | ||
// are implemented, there is no other way to prevent composed input. | ||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1022204 | ||
let { | ||
value, | ||
selectionStart, | ||
selectionEnd | ||
} = inputRef.current; | ||
compositionStartState.current = { | ||
value, | ||
selectionStart, | ||
selectionEnd | ||
}; | ||
}, | ||
onCompositionEnd() { | ||
if (!state.validate(inputRef.current.value)) { | ||
// Restore the input value in the DOM immediately so we can synchronously update the selection position. | ||
// But also update the value in React state as well so it is correct for future updates. | ||
let { | ||
value, | ||
selectionStart, | ||
selectionEnd | ||
} = compositionStartState.current; | ||
inputRef.current.value = value; | ||
inputRef.current.setSelectionRange(selectionStart, selectionEnd); | ||
state.setInputValue(value); | ||
} | ||
} | ||
}), | ||
labelProps | ||
}; | ||
} | ||
exports.useFormattedTextField = useFormattedTextField; | ||
//# sourceMappingURL=main.js.map |
@@ -0,1 +1,2 @@ | ||
import { useEffect, useRef } from "react"; | ||
import { useLabel } from "@react-aria/label"; | ||
@@ -71,2 +72,136 @@ import { useFocusable } from "@react-aria/focus"; | ||
} | ||
function $c7e32867b2abc7e2d776d5c060056cb7$var$supportsNativeBeforeInputEvent() { | ||
return typeof window !== 'undefined' && window.InputEvent && // @ts-ignore | ||
typeof InputEvent.prototype.getTargetRanges === 'function'; | ||
} | ||
export function useFormattedTextField(props, state, inputRef) { | ||
let stateRef = useRef(state); | ||
stateRef.current = state; // All browsers implement the 'beforeinput' event natively except Firefox | ||
// (currently behind a flag as of Firefox 84). React's polyfill does not | ||
// run in all cases that the native event fires, e.g. when deleting text. | ||
// Use the native event if available so that we can prevent invalid deletions. | ||
// We do not attempt to polyfill this in Firefox since it would be very complicated, | ||
// the benefit of doing so is fairly minor, and it's going to be natively supported soon. | ||
useEffect(() => { | ||
if (!$c7e32867b2abc7e2d776d5c060056cb7$var$supportsNativeBeforeInputEvent()) { | ||
return; | ||
} | ||
let input = inputRef.current; | ||
let onBeforeInput = e => { | ||
let state = stateRef.current; // Compute the next value of the input if the event is allowed to proceed. | ||
// See https://www.w3.org/TR/input-events-2/#interface-InputEvent-Attributes for a full list of input types. | ||
let nextValue; | ||
switch (e.inputType) { | ||
case 'historyUndo': | ||
case 'historyRedo': | ||
// Explicitly allow undo/redo. e.data is null in this case, but there's no need to validate, | ||
// because presumably the input would have already been validated previously. | ||
return; | ||
case 'deleteContent': | ||
case 'deleteByCut': | ||
case 'deleteByDrag': | ||
nextValue = input.value.slice(0, input.selectionStart) + input.value.slice(input.selectionEnd); | ||
break; | ||
case 'deleteContentForward': | ||
// This is potentially incorrect, since the browser may actually delete more than a single UTF-16 | ||
// character. In reality, a full Unicode grapheme cluster consisting of multiple UTF-16 characters | ||
// or code points may be deleted. However, in our currently supported locales, there are no such cases. | ||
// If we support additional locales in the future, this may need to change. | ||
nextValue = input.selectionEnd === input.selectionStart ? input.value.slice(0, input.selectionStart) + input.value.slice(input.selectionEnd + 1) : input.value.slice(0, input.selectionStart) + input.value.slice(input.selectionEnd); | ||
break; | ||
case 'deleteContentBackward': | ||
nextValue = input.selectionEnd === input.selectionStart ? input.value.slice(0, input.selectionStart - 1) + input.value.slice(input.selectionStart) : input.value.slice(0, input.selectionStart) + input.value.slice(input.selectionEnd); | ||
break; | ||
default: | ||
if (e.data != null) { | ||
nextValue = input.value.slice(0, input.selectionStart) + e.data + input.value.slice(input.selectionEnd); | ||
} | ||
break; | ||
} // If we did not compute a value, or the new value is invalid, prevent the event | ||
// so that the browser does not update the input text, move the selection, or add to | ||
// the undo/redo stack. | ||
if (nextValue == null || !state.validate(nextValue)) { | ||
e.preventDefault(); | ||
} | ||
}; | ||
input.addEventListener('beforeinput', onBeforeInput, false); | ||
return () => { | ||
input.removeEventListener('beforeinput', onBeforeInput, false); | ||
}; | ||
}, [inputRef, stateRef]); | ||
let onBeforeInput = !$c7e32867b2abc7e2d776d5c060056cb7$var$supportsNativeBeforeInputEvent() ? e => { | ||
let nextValue = e.target.value.slice(0, e.target.selectionStart) + e.data + e.target.value.slice(e.target.selectionEnd); | ||
if (!state.validate(nextValue)) { | ||
e.preventDefault(); | ||
} | ||
} : null; | ||
let { | ||
labelProps, | ||
inputProps: textFieldProps | ||
} = useTextField(props, inputRef); | ||
let compositionStartState = useRef(null); | ||
return { | ||
inputProps: mergeProps(textFieldProps, { | ||
onBeforeInput, | ||
onCompositionStart() { | ||
// Chrome does not implement Input Events Level 2, which specifies the insertFromComposition | ||
// and deleteByComposition inputType values for the beforeinput event. These are meant to occur | ||
// at the end of a composition (e.g. Pinyin IME, Android auto correct, etc.), and crucially, are | ||
// cancelable. The insertCompositionText and deleteCompositionText input types are not cancelable, | ||
// nor would we want to cancel them because the input from the user is incomplete at that point. | ||
// In Safari, insertFromComposition/deleteFromComposition will fire, however, allowing us to cancel | ||
// the final composition result if it is invalid. As a fallback for Chrome and Firefox, which either | ||
// don't support Input Events Level 2, or beforeinput at all, we store the state of the input when | ||
// the compositionstart event fires, and undo the changes in compositionend (below) if it is invalid. | ||
// Unfortunately, this messes up the undo/redo stack, but until insertFromComposition/deleteByComposition | ||
// are implemented, there is no other way to prevent composed input. | ||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1022204 | ||
let { | ||
value, | ||
selectionStart, | ||
selectionEnd | ||
} = inputRef.current; | ||
compositionStartState.current = { | ||
value, | ||
selectionStart, | ||
selectionEnd | ||
}; | ||
}, | ||
onCompositionEnd() { | ||
if (!state.validate(inputRef.current.value)) { | ||
// Restore the input value in the DOM immediately so we can synchronously update the selection position. | ||
// But also update the value in React state as well so it is correct for future updates. | ||
let { | ||
value, | ||
selectionStart, | ||
selectionEnd | ||
} = compositionStartState.current; | ||
inputRef.current.value = value; | ||
inputRef.current.setSelectionRange(selectionStart, selectionEnd); | ||
state.setInputValue(value); | ||
} | ||
} | ||
}), | ||
labelProps | ||
}; | ||
} | ||
//# sourceMappingURL=module.js.map |
import { AriaTextFieldProps } from "@react-types/textfield"; | ||
import { InputHTMLAttributes, LabelHTMLAttributes, RefObject, TextareaHTMLAttributes, ElementType } from "react"; | ||
interface TextFieldAria { | ||
export interface TextFieldAria { | ||
/** Props for the input element. */ | ||
@@ -24,3 +24,8 @@ inputProps: InputHTMLAttributes<HTMLInputElement> | TextareaHTMLAttributes<HTMLTextAreaElement>; | ||
export function useTextField(props: AriaTextFieldOptions, ref: RefObject<HTMLInputElement | HTMLTextAreaElement>): TextFieldAria; | ||
interface FormattedTextFieldState { | ||
validate: (val: string) => boolean; | ||
setInputValue: (val: string) => void; | ||
} | ||
export function useFormattedTextField(props: AriaTextFieldProps, state: FormattedTextFieldState, inputRef: RefObject<HTMLInputElement>): TextFieldAria; | ||
//# sourceMappingURL=types.d.ts.map |
{ | ||
"name": "@react-aria/textfield", | ||
"version": "3.0.0-nightly.992+d1844573", | ||
"version": "3.0.0-nightly.994+faa93480", | ||
"description": "Spectrum UI components in React", | ||
@@ -21,7 +21,7 @@ "license": "Apache-2.0", | ||
"@babel/runtime": "^7.6.2", | ||
"@react-aria/focus": "3.0.0-nightly.992+d1844573", | ||
"@react-aria/label": "3.0.0-nightly.992+d1844573", | ||
"@react-aria/utils": "3.0.0-nightly.992+d1844573", | ||
"@react-types/shared": "3.0.0-nightly.992+d1844573", | ||
"@react-types/textfield": "3.0.0-nightly.992+d1844573" | ||
"@react-aria/focus": "3.0.0-nightly.994+faa93480", | ||
"@react-aria/label": "3.0.0-nightly.994+faa93480", | ||
"@react-aria/utils": "3.0.0-nightly.994+faa93480", | ||
"@react-types/shared": "3.0.0-nightly.994+faa93480", | ||
"@react-types/textfield": "3.0.0-nightly.994+faa93480" | ||
}, | ||
@@ -34,3 +34,3 @@ "peerDependencies": { | ||
}, | ||
"gitHead": "d1844573fa992f7a34285ebafdc08cb407829095" | ||
"gitHead": "faa9348097bf62403e37e32daf897cdad5512e09" | ||
} |
@@ -14,1 +14,2 @@ /* | ||
export * from './useTextField'; | ||
export * from './useFormattedTextField'; |
@@ -20,3 +20,3 @@ /* | ||
interface TextFieldAria { | ||
export interface TextFieldAria { | ||
/** Props for the input element. */ | ||
@@ -23,0 +23,0 @@ inputProps: InputHTMLAttributes<HTMLInputElement> | TextareaHTMLAttributes<HTMLTextAreaElement>, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
80940
12
667