h2oai / wave

Realtime Web Apps and Dashboards for Python and R
https://wave.h2o.ai
Apache License 2.0
3.9k stars 323 forks source link

Pass a type hint with textboxes to enable keyboard switching to numerical on mobile devices (iPhone) #2170

Closed buhaytza2005 closed 7 months ago

buhaytza2005 commented 8 months ago

Is your feature request related to a problem? Please describe

When creating a form that requires filling in numerical values, the user must change manually to a numerical keyboard.

Describe the solution you'd like

A way to describe a textbox as requiring numbers or providing a type hint to improve the usability of forms (A clear and concise description of what you want to happen.)

Describe alternatives you've considered

I considered using spinners but when dealing with numbers where the range is high (for ex 1-100) this quickly becomes tiring.

mturoci commented 8 months ago

Add a new type attribute to ui.textbox. Possible values: text | number | tel, defaulting to text.

buhaytza2005 commented 8 months ago

I started working on this and as sugested, introduced a type attribute - my syntax highlighting is acting a bit funky in python - l guess because of the global type even though it is not a reserved word. image

On the component side, passed the type along and got the below output - which is the desired behaviour.

image

export const
  XTextbox = ({ model: m }: { model: Textbox }) => {
    const
      [value, setValue] = React.useState(m.value ?? ''),
      debounceRef = React.useRef(debounce(DEBOUNCE_TIMEOUT, wave.push)),
      onChange = ({ target }: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, v?: S) => {
        v = v || (target as HTMLInputElement).value

        wave.args[m.name] = v ?? (m.value || '')
        if (m.trigger) debounceRef.current()
        setValue(v)
        m.value = v
      },
      textFieldProps: Fluent.ITextFieldProps & { 'data-test': S } = {
        'data-test': m.name,
        label: m.label,
        value,
        errorMessage: m.error,
        required: m.required,
        disabled: m.disabled,
        readOnly: m.readonly,
        onChange,
        iconProps: m.icon ? { iconName: m.icon } : undefined,
        placeholder: m.placeholder,
        prefix: m.prefix,
        suffix: m.suffix,
        multiline: m.multiline,
        spellCheck: m.spellcheck,
        type: m.password ? 'password' : (m.type || 'text'),
        /* type: m.password ? 'password' : 'text',*/
        /* inputMode: (m.type == 'number' || m.type == 'tel') ? 'numeric': undefined,*/
      }

    React.useEffect(() => {
      wave.args[m.name] = m.value ?? ''
      setValue(m.value ?? '')
    }, [m.value, m.name])

    return m.mask
      ? <Fluent.MaskedTextField mask={m.mask} {...textFieldProps} />
      : (
        <Fluent.TextField
          styles={
            m.multiline && m.height && !m.height.endsWith('%')
              ? { field: { height: m.height }, fieldGroup: { minHeight: m.height } }
              : undefined
          }
          {...textFieldProps}
        />
      )
  }

Of course things can't be straightforward and the number input now has arrows like a spinbox and increments/decrements based on scroll behaviour.

image

I have tried to use the inputMode instead or combined with and I keep getting the same result. Came across some posts where the spinbox was hidden via CSS and there was an event handler implemented on the scrool event but that seems a bit messy.

I have the following queries:

mturoci commented 8 months ago

is type ok as a name even though LSPs are likely to no like it?

Feel free to suggest alternatives. I agree thattype could be troublesome, especially in the future.

is there any clean way of having the desired behaviour on mobiles and still retain a simple textbox on desktop? Any resources you can point me to?

What is the desired behavior in this case? Would hiding the arrows be enough? On second thought, wondering whether ui.spinbox wouldn't be a better solution than textbox with type number. Would it open the numeric keyboard on mobile?

buhaytza2005 commented 8 months ago

Desired behaviour would be for the keyboard to react as it does but not have the arrows and not engage on scroll up or down.

Would need to check what it would do if both a textbox and a spinner are part of the form as a blanket css change as the first link suggests might alter the behaviour of the spinbox if the selector is not too specific.

https://www.geeksforgeeks.org/how-to-hide-number-input-spin-box-using-css/amp/

I tried to add the behaviour through inputmode (as suggested in the below article) but it doesn't seem to get passed through as an attribute to the rendered html even though it exists as a property.

https://css-tricks.com/finger-friendly-numerical-inputs-with-inputmode/

I'll keep digging to see if I'm not passing it correctly.

Is there any chance the transformation from tsx to python to generate elements or viceversa is ignoring it?

mturoci commented 8 months ago

I tried to add the behaviour through inputmode (as suggested in the below article) but it doesn't seem to get passed through as an attribute to the rendered html even though it exists as a property.

I wouldn't go for inputmode as it's not supported in Safari.

Let's add the needed attribute and then hack around the undesired buttons/scroll via a little bit of custom CSS and JS that I can send you. I would prefer to not bake this into Wave for now.

buhaytza2005 commented 8 months ago

I wouldn't go for inputmode as it's not supported in Safari.

Understood

Let's add the needed attribute and then hack around the undesired buttons/scroll via a little bit of custom CSS and JS that I can send you. I would prefer to not bake this into Wave for now.

Just to clarify, proceed with a PR adding the attribute - with a different name - but leave the number input with the arrows?

mturoci commented 8 months ago

Just to clarify, proceed with a PR adding the attribute - with a different name - but leave the number input with the arrows?

Exactly

marek-mihok commented 7 months ago

On iOS with type: 'number' you are getting this keyboard layout:

image

@buhaytza2005 If you would like to show your desired keyboard with large buttons (as on the example you provided), the pattern: "[0-9]*" attribute needs to be specified as well:

image

However, with the second option you are not getting possibility to enter numbers with decimal point (.,) and there is even no option to change a keyboard. But on Android, you are getting the similar numpad layout, also with the option to enter decimal numbers.

As we should be consistent across platforms (not allowing more input options one one platform than on the other), I would go with the first option, so with type: 'number' only. WDYT @mturoci ?

mturoci commented 7 months ago

Yea, let's go with type='number'. Thanks @marek-mihok!