How to know if a dialog is closed with AltKey

Using showModal and Preact, I am creating a function similar to Illustrator’s translate dialog. I have enclosed the dialog in a form so that it can be exited by pressing the return key, but I also want it to be in duplicate mode when the AltKey is pressed.

However, onSubmit event of the form does not seem to have the altKey information. Is there any way to know if the user exited by pressing the AltKey without using onClick?

Similar Posts

Showing Dialog

let dialogElement = null ;
if(!dialogElement){
  dialogElement = document.createElement('dialog') ;
  render(<App dialog={dialogElement} bounds={bounds} />, dialogElement) ;
}
document.body.appendChild(dialogElement) ;

const result = await dialogElement.showModal({
  title: 'Translate',
  resize: 'none',
}) ;
dialogElement.remove() ;
if(result === 'reasonCanceled') {return ;}
console.log(result) ;

Building Dialog

<form
   method='dialog'
   onSubmit={ (event) => {
     event.preventDefault() ;
     let mode = 'translate' ;
     if(event.altKey) {mode = 'duplicate' ;} // event.altKey is undefined
     dialog.close({action: mode, value: returnData()}) ;
   } }
>
...
sp-textfield, sp-button, etc. 
...
</form>

Check this SO reply

Basically submit event has no idea about keys. Change your form to something like:

<form method='dialog'>
...
sp-textfield, sp-button, etc. 
...
<button onClick={ (event) => {
     event.preventDefault() ;
     let mode = 'translate' ;
     if(event.altKey) {mode = 'duplicate' ;} // event.altKey is undefined
     dialog.close({action: mode, value: returnData()}) ;
   } }
>Submit</button>
1 Like

Thank you for your response.
I was able to achieve this by changing it to an onKeyDown event based on the referenced document.

<form
  method='dialog'
  onKeyDown={ (event) => {
     event.preventDefault() ;
     let mode = 'translate' ;
     if(event.altKey) {mode = 'duplicate' ;}
     if(event.key === 'Enter') {dialog.close({action: mode, value: returnData()}) ;}
  } }
>
...
sp-textfield, sp-button, etc. 
...
</form>

Do you really want this event to trigger every time someone types something to input on that form? Or otherwise interact with form by using keyboard :thinking: Here’s a fiddle which shows in console when event is triggered - just start typing to the input

Every time I type, it logs

Of course, it is best if events happen as and when needed. I consider it acceptable for the following reasons in this case.

  • The requirement is that the user can work without clicking in this plugin
  • Since this form is in a modal dialog, it only burdens Photoshop while it is being displayed
  • The user wants to enter only numbers or expressions, not sentences containing line breaks

It’s your plugin and for you to decide, but I still don’t understand why would you want to listen for every single keystroke when there’s a much more appropriate solution :thinking: Anyway, glad it works for you

Sorry, I am not familiar with web technology or English so I may have misunderstood something.

I figured the first method you gave me would require actual clicking of the button. I wanted to eliminate the clicking so I looked for another way.

But you mean to say that it is a mistake and the return key triggers the onClick event without actually clicking the button, plus I can see if the AltKey is pressed?

I can get close when the button has focus, but altKey always seemed to return false. Besides, I would like the sp-textfield to have focus when the dialog is opened.

Did you try it or just decided it won’t work? Pressing Enter/Return works just fine. That <button/> works exactly the same as submit and in suggested case it also catches Alt/Opt key


Exactly
Well… At least it should work, but that’s UXP, so… :man_shrugging: On normal browsers it works


This has nothing to do with form submission and should be implemented sepaarately

I made a decision without trying it first, so I tried it this time. The results are as follows:

  • It was possible to terminate the dialog with the return key
  • onClick specified for the button element does not seem to be triggered
  • The result of the dialog is always an empty string
  • The result is the same even if the button or form has a value attribute.
  • altKey is always false even if the button element is actually clicked (sp-button works fine)

It could not be achieved with my current understanding.

<form method='dialog'>
  ...
  sp-textfield, sp-button, etc. 
  ...
  
  <button
    onClick={ (event) => { // not happen
      event.preventDefault() ;
      console.log(event.altKey) ;
      let mode = 'translate' ;
      if(event.altKey) {mode = 'duplicate' ;}
      dialog.close({action: mode, value: returnData()}) ;
    } }
  >
     Submit
  </button>
</form>

OK, just got back after work and tested also. Can’t make it work. Probably another UXP bug (please, @kerrishotts, don’t tell it’s just a limitation, when it clearly is a bug :frowning:).

Not only key is not caught, but even pressing Enter on input doesn’t submit a form…

HTML:

<form method="dialog">
  <input type="text"/>
  <button type="submit" id="formTestButton" >Submit</button>
  <sp-body id="formTestResult"></sp-body>
</form>

JS:

document.querySelector("#formTestButton").addEventListener("click", (event) => {
  event.preventDefault();

  console.log(event.altKey)

  let mode = 'translate'
  if(event.altKey) {mode = 'duplicate'}
  document.querySelector("#formTestResult").textContent = mode
})

Here’s the fiddle
Actually couldn’t make it submit on Alt+Enter, but it works perfectly fine with Shift, while on UXP it doesn’t work at all

1 Like

Given a dialog of the form:

<dialog id="dlg">
  <form method="dialog">
    <sp-textfield type="text"></sp-textfield>
    <sp-button type="submit" id="formTestButton">Submit</sp-button>
  </form>
</dialog>

You can use this JS to respond to normal or alternate dismissal:

function decorateDialogWithAltKeySubmission(theDialog, {normal, alternate} = {}) {
  const theDialogForm = theDialog.querySelector("form");

  // if dialog has been decorated before, bail
  if (theDialog.dataset.decorated) return;

  // keep track of user's intent throughout key presses, clicks, and submits
  let theDialogReturnValue = normal;

  // detect if ALT is pressed
  function setDialogReturnValue(event) {
      if (event.altKey === undefined) return; // ignore events where this
                                              // isn't set so we don't
                                              // forget the user intent
      // pick the right return value based on altKey
      theDialogReturnValue = event.altKey ? alternate : normal;
  }

  // if user presses ENTER, we want a chance to see if ALT is pressed
  theDialogForm.addEventListener("keydown", event => {
    if (event.key !== "Enter") return; // bail if ENTER isn't pressed
    setDialogReturnValue(event);
  });

  // If the form is submitted, forward along our return value
  theDialogForm.addEventListener("submit", (event) => {
    event.preventDefault(); // prevent default blank (""_ return value
    theDialog.close(theDialogReturnValue);
  });

  // sp-buttons don't automatically act as submit buttons, so 
  // check for ALT here and close
  theDialogForm.querySelector("sp-button[type=submit]")
    .addEventListener("click", event => {
    setDialogReturnValue(event);
    theDialog.close(theDialogReturnValue);
  });

  // don't decorate the dialog again
  theDialog.dataset.decorated = "yes"; 
}

Then “decorate” the dialog with:

const theDialog = document.querySelector("#dlg");
decorateDialogWithAltKeySubmission(theDialog , {
  normal: "translate", // return if ALT is not pressed
  alternate: "duplicate" // return if ALT is pressed
});

Then when you show and get a response, you’ll get reasonCanceled (ESC or other dismissal), the “normal” response (translate) if ALT is not held, and the “alternate” response (duplicate) if ALT is held.

const response = await theDialog.uxpShowModal({
    title: "Title",
    size: { width: 480, height: 320 }
})

if (response === "reasonCanceled") return;
if (response === "translate") { /* do translate stuff */ }
if (response === "duplicate") { /* do duplicate stuff */ }

Some other observations:

  • <button> in PS doesn’t send UXP any modifiers. As such, altKey (et al) will all be false. Yes, I’d call it a bug, but the workaround is to use sp-button.
  • <sp-button>, however, is controlled by UXP and does send modifiers. altKey will contain the state of the Alt key (whatever that is for the OS in question)
  • ctrl+click has lots of funky behavior in Ps – on a <button>, submission will happen, but you won’t see ctrlKey === true (it’ll be false). On an <sp-button>, ctrl+click won’t submit or send a click event at all.
  • As such, your most reliable option is <sp-button> and avoid the ctrl key.
1 Like

I tried decorateDialog and it worked as expected.

We regressed to onKeyDown, but I was able to learn the desirable and realistically usable specifications.

Thank you, @Karmalakas and @kerrishotts.

Awesome example actually, but…

Why most UI and user interaction on UXP is made so difficult to achieve? Only via a huge amount of workarounds. It feels like teams, working on this, do not communicate with each other at all. Each team tries to invent something different and these inventions do not work together. Overall it seems like the wheel is being invented all over again. Why couldn’t Adobe take what’s already there and strip down what they want to limit access to, instead of reinventing a browser by building bits from scratch that just mostly do not work as expected?

That is beyond my understanding :confused: