Skip to content

Commit

Permalink
feat(InputMasked): enhance inputMode for better support of showing co…
Browse files Browse the repository at this point in the history
…rrect soft keyboards (#3108)

For iOS we use a custom trick (me and Joakim found during a peer prog
session).

This should solve #3097

Co-authored-by: Joakim Bjerknes <[email protected]>
  • Loading branch information
tujoworker and joakbjerk authored Dec 19, 2023
1 parent cf04125 commit e95ef1f
Show file tree
Hide file tree
Showing 10 changed files with 438 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ Both entering a comma or a dot will act as a decimal separator if [decimals are

#### InputMode

For mobile devices and soft keyboards, the HTML input element does support a numeric-only keyboard. But sadly it does not support negative values at the time of writing this. So it is only enabled if `allowNegative` is set to false.
**NB:** Please do not set `inputMode="numeric"` or `inputMode="decimal"`, because devices may or may not show a minus key (`-`)!

The InputMasked component does handle soft keyboards (iOS and Android) by using either `inputMode="numeric"` and `inputMode="decimal"` depending on `allowNegative` and `allowDecimal` (getSoftKeyboardAttributes).

For iOS it additionally sets `type="number"` during focus (InputModeNumber). This way the correct numeric soft keyboard is shown.

<InputMaskedInfoInputMode />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
handleCurrencyMask,
handleNumberMask,
correctCaretPosition,
getInputModeFromMask,
getSoftKeyboardAttributes,
handleThousandsSeparator,
handleDecimalSeparator,
fromJSON,
Expand Down Expand Up @@ -230,11 +230,6 @@ export const useInputElement = () => {
// Set ref for Eufemia input
innerRef.current = ref.current

// Set "inputMode"
if (!params.inputMode) {
params.inputMode = getInputModeFromMask(mask)
}

return (
<TextMask
inputRef={ref}
Expand All @@ -245,6 +240,7 @@ export const useInputElement = () => {
guide={showGuide}
keepCharPositions={keepCharPositions}
placeholderChar={placeholderChar}
{...getSoftKeyboardAttributes(mask)}
{...params}
className={classnames(
params.className,
Expand Down Expand Up @@ -441,7 +437,11 @@ const useCallEvent = ({ setLocalValue }) => {
!props.selectall
) {
// Also correct here, because of additional click inside the field
correctCaretPosition(event.target, maskParams, props)
event.target.runCorrectCaretPosition = () =>
correctCaretPosition(event.target, maskParams, props)
if (!event.target.__getCorrectCaretPosition) {
event.target.runCorrectCaretPosition()
}
}

return result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
getThousandsSeparator,
} from '../number-format/NumberUtils'
import { warn } from '../../shared/component-helper'
import { IS_ANDROID, IS_IOS } from '../../shared/helpers'
import { IS_IOS } from '../../shared/helpers'
import { safeSetSelection } from './text-mask/createTextMaskInputElement'

const enableLocaleSupportWhen = ['as_number', 'as_percent', 'as_currency']
Expand Down Expand Up @@ -332,25 +332,29 @@ export const handleNumberMask = ({ mask_options, number_mask }) => {
}

/**
* Returns the type of what inputMode should be used
* Returns the type of what inputMode or type attribute should be used
*
* @param {function} mask mask function
* @returns undefined|decimal|numeric
*/
export function getInputModeFromMask(mask) {
export function getSoftKeyboardAttributes(mask) {
if (mask?.instanceOf !== 'createNumberMask') {
return undefined
}

const maskParams = mask?.maskParams

// because of the missing minus key, we still have to use text on Android and iOS
if ((IS_ANDROID || IS_IOS) && maskParams?.allowNegative !== false) {
// because of the missing minus key, we still have to use text on iOS
if (IS_IOS && maskParams?.allowNegative !== false) {
return undefined
}

if (maskParams && mask?.instanceOf === 'createNumberMask') {
return maskParams.allowDecimal && maskParams.decimalLimit !== 0
? 'decimal'
: 'numeric'
return {
inputMode:
maskParams.allowDecimal && maskParams.decimalLimit !== 0
? 'decimal'
: 'numeric',
}
return undefined
}

/**
Expand Down
16 changes: 14 additions & 2 deletions packages/dnb-eufemia/src/components/input-masked/TextMask.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import createTextMaskInputElement from './text-mask/createTextMaskInputElement'
import InputModeNumber from './text-mask/InputModeNumber'
import { isNil } from './text-mask/utilities'

export default class TextMask extends React.PureComponent {
Expand Down Expand Up @@ -47,17 +48,28 @@ export default class TextMask extends React.PureComponent {
this.initTextMask()
}

componentWillUnmount() {
this.inputMode?.remove()
}

initTextMask() {
const {
props,
props: { value },
props: { value, inputMode },
} = this

const inputElement = this.inputRef.current
this.textMaskInputElement = createTextMaskInputElement({
...props,
inputElement: this.inputRef.current,
inputElement,
})
this.textMaskInputElement.update(value)

if (!inputMode && inputMode !== 'none') {
this.inputMode = new InputModeNumber()
}

this.inputMode?.setElement(inputElement)
}

onChange = (event) => {
Expand Down
Loading

0 comments on commit e95ef1f

Please sign in to comment.