With InDesign, sp-textfield does not trigger change event

,

I am trying to create a text input field with a calculation function. Using a component previously made for Photoshop, I noticed that for some reason it does not trigger the change event.

I need this event separate from the input event because I want the calculation to be performed the moment the user confirms the input or removes focus from the textfield.

Is there any way to force the change event to happen or alternatively achieve this purpose?

import { h } from 'preact' ;
import '@spectrum-web-components/theme/sp-theme.js' ;
import '@spectrum-web-components/theme/src/themes.js' ;

export const App = () => {
  return (
    <sp-theme
      theme='spectrum'
      color='light'
      scale='medium'
    >
      <sp-textfield
        value='0'
        onChange={ (event) => {
          console.log(event) ;
        } }
      ></sp-textfield>
    </sp-theme>
  ) ;
} ;

Environment

  • macOS 12.6.7 (Apple Silicon)
  • InDesign 2023 (18.5.0.57) / InDesign 2024 (Prerelease) (19.0.0.123)
  • UXP Plugin
  • Preact 10.16.0
  • sp-textfield in Spectrum UXP Widgets

What can be done

  • Can capture input events
  • Capture keydown events and trigger blur on enter key

Things tried that didn’t work

  • Use React instead of Preact
  • Use WC components provided by Adobe in Preact and React
  • Place them outside of sp-theme
  • Changing to sp-textarea or input type=“text”
<input
      ref={ref}
      autoFocus
      type="text"
      className="name-edit"
      value={currentValue}
      onKeyUp={(ev) => {
        if (ev.key === 'Enter') {
          setOpen(false);
          return;
        }
        if (ev.key === 'Escape') {
          setCurrentValue(text);
          setOpen(false);
        }
      }}
      onChange={(ev) => {
        console.log(ev);
        setCurrentValue(ev.target.value);
      }}
      onBlur={(ev) => {
        setOpen(false);
      }}></input>

This seems to be working just fine. but <sp-text> doesn’t

2 Likes

Thank you for your response, confirming that it does not work with sp-textfield.

I tried and onKeyUp responded, but onChange and onBlur were unresponsive. Is this your intended result? Or is onChange also working in your environment?

Now I’m using a similar approach to what you are doing with onKeyUp to get around the limitation. When onKeyDown and the key is Enter, Escape, or Tab, I do what onChange should to do.

sp-textfield is a custom tag and is implemented similar to a web component inside UXP. React has issues with adding event listeners on web components.

From React Documentation:

Events emitted by a Web Component may not properly propagate through a React render tree. You will need to manually attach event handlers to handle these events within your React components.

You can use addEventListener instead of onChange. The below code works.

import React, {useRef, useEffect} from 'react';

export const Demos = () => {
    const inputRef = useRef(null);
    useEffect(() => {
        inputRef.current ?. addEventListener("change", (e) => {
            console.log("change event fired when event listener is added", e);
        })
    }, []);
    return (<>
        <sp-textfield ref={inputRef}
            onChange={
                (event) => {
                    console.log("change fired");
                }
            }
            onInput={
                (event) => {
                    console.log("input event fired")
                }
        }></sp-textfield></>)
}

Reference: https://legacy.reactjs.org/docs/web-components.html

@Majji Thanks for the info. Are you successful with this code? I tried it and input responded, but change did not.

I think that the mechanism is the same as WC component, so if it succeeds in this way, it will also succeed with WC. What do you think?

@sttk3 In the above code, I was able to print “change event fired when event listener is added” log and wasn’t able to print “change fired” log which is understandable as per React limitations. You can remove the onChange and make your code work with addEventListener. Input event was working either way with or without addEventListener.

1 Like

@Majji Thanks, I will try again.

1 Like

It has been a long time.

In the end I could not get the change event in InDesign, even with addEventListener. It might have something to do with the fact that change events in React are equivalent to input events in Preact. Another reason may be that clicking anywhere on the panel does not take focus away from the text field.

Therefore, I defined the following two processes

  1. when the ‘Enter’, ‘Tab’, or ‘Escape’ key is down, execute the calculation that is planned to perform in the change event and remove the focus from the text field
  2. when a user clicks anywhere on the panel, execute the same process.

This could be a workaround.

// preact
import { h } from 'preact' ;
import { useRef, useState } from 'preact/hooks' ;

// adobe
import '@spectrum-web-components/theme/sp-theme.js' ;
import '@spectrum-web-components/theme/src/themes.js' ;

export const App = () => {
  const fieldRef = useRef(null) ;
  const [fieldValue, setFieldValue] = useState('0') ;

  const calc = (text) => {
    // write calculation process here
    console.log(`calc '${text} + 1'`) ;
    const res = Number(text) + 1 ;
    return res ;
  } ;

  return (
    <sp-theme
      theme='spectrum'
      color='light'
      scale='medium'
    >
      <div
        class='click-area'
        onClick={ (event) => {
          console.log('click --> treat as change') ;
          const num = calc(fieldRef.current.value) ;
          setFieldValue(num.toString()) ;
          fieldRef.current.blur() ;
        } }
      >
        <sp-textfield
          ref={fieldRef}
          value={fieldValue}
          onKeyDown={ (event) => {
            event.preventDefault() ;
            const eventKey = event.key ;
            if(['Enter', 'Tab', 'Escape'].includes(eventKey)) {
              console.log('keyDown --> treat as change') ;
              const num = calc(event.target.value) ;
              setFieldValue(num.toString()) ;
              event.target.blur() ;
            } else {
              // skip
            }
          } }
        ></sp-textfield>
      </div>
    </sp-theme>
  ) ;
} ;
1 Like

Could it be the same as in Photoshop, that you need a wrapper component for events to work? I use wc-react myself. Search forum for “wrapper”

This seems to be an InDesign specific issue. It is not caused by web components. Even if it is just <input type='text'> and Vanilla JS, it does not trigger the change event.

Doesn’t work for me either. I use the standard widget.
Hint: I used “blur” instead. In many cases this should be sufficient.