TiddlyWiki / TiddlyWiki5

A self-contained JavaScript wiki for the browser, Node.js, AWS Lambda etc.
https://tiddlywiki.com/
Other
8.06k stars 1.19k forks source link

Enhance modals #1215

Closed tobibeer closed 3 years ago

tobibeer commented 9 years ago

Allow to hit ESC or click anywhere outside the modal to close the topmost one. Allow to make the modal ui entirely customizable, incl. headers & footers. This will allow to implement custom controls & ui's, i.e. lightboxes more easily.

tobibeer commented 9 years ago

background: http://ibox.tiddlyspot.com/#Examples

Here you can clearly see the limitations in terms of both handling the modal as well as theming it, providing additional features, such as « prev next » buttons (alongside the close button), the title being but only the template title (captions won't help), etc...

Also, how to avoid those <p> margins in a modal template? ...they are stealing away precious real-estate.

felixhayashi commented 9 years ago

Just found this by accident and I just wanted to express that TiddlyWiki should not have modals closed with the escape key. I have many modals containing multiple buttons (choices) and one needs necessarily be selected. If a user can close a modal per escape, my program would begin to hang as it waits for an answer.

Related to #1499

Jermolene commented 9 years ago

I think that all of the features that @tobibeer suggests would need to be optional for backwards compatibility.

tobibeer commented 9 years ago

as for customization, there could be some "content=some_tid_processing_placeholdersor'system'_variables"

as for canceling via ESC, I think that should always leave a modal of level X, i.e. if you use a modal to progress in a workflow — through a sequence of steps — then you're essentially chosing the wrong tool (if you were to rely on the modal not being closed so as to not be "hung up" in any given step) ...in that sense, if you close a modal that is used for some multi-step-workflow... then you pretty much cancel out of the workflow entirely if you chose to ESC out of the corresponding modal (assuming the workflow is linear)

felixhayashi commented 9 years ago

if you use a modal to progress in a workflow — through a sequence of steps — then you're essentially chosing the wrong tool (if you were to rely on the modal not being closed so as to not be "hung up" in any given step)

Sorry @tobibeer but this is dangerous. The decision how a modal is closed should be passed by the process that triggers the modal message. Otherwise it has the potential to completly break plugins like e.g. TiddlyMap that use modals as part of the workflow. Allowing to close a modal via X is not the problem, the problem is that the process that invoked the modal is not informed about the closing of the modal!

The problem is that when TW would close a modal and e.g. a plugin like TiddlyMap doesn't know the modal is now closed, the plugin will continue waiting and might reach an undefined state. I have no chance to "cancel out of the workflow entirely" as you expressed it.

It would be acceptable to use ESC for closing if the tm-modal message were enhanced in a way that another event parameter is introduced that defines whether the modal can be closed via outside-click and also invokes a callback of the process that opened the modal so this process knows that the modal has been closed and gets a chance to cancel out further actions or reset its state.

So I am not against implementing a close-by-click mechanism but if the control-flow of the modal cannot be controlled by the process that invoked the modal it will be a nightmare for advanced plugins like TiddlyMap that heavily rely on modals.

-Felix

tobibeer commented 9 years ago

So I am not against implementing a close-by-click mechanism but if the control-flow of the modal cannot be controlled by the process that invoked the modal it will be a nightmare for advanced plugins like TiddlyMap that heavily rely on modals.

I sure agree with that... whatever invokes a modal should be given a chance to intercept its closing.

danielo515 commented 9 years ago

Hello @felixhayashi ,

I don't know why, but I was expecting your input :tongue: How are you currently managing the close of the modals? as far as I know, TW adds a close button by default which does not calls any "callback" method.

In my opinion modals are very handy, and they deserve more love an attention. The current implementation has not changed for a very long time.

felixhayashi commented 9 years ago

@tobibeer

I sure agree with that... whatever invokes a modal should be given a chance to intercept its closing.

Ah ok. Then we are on the same side ;)

@danielo515

I don't know why, but I was expecting your input

Hehe, well Dialogs are important for TiddlyMap so of course I will say something when it comes to modals :)

How are you currently managing the close of the modals? as far as I know, TW adds a close button by default which does not calls any "callback" method.

Ok, I'll try to summarize it:

TiddlyMap has a DialogManager class that is responsible for invoking dialogs.

It is a bit complicated but basically it works like this:

Say a user does something in the map which triggers an event which is delegated to a handler handleUserActionXYZ. This handler-process prepares the dialog and calls the open() method of the DialogManager:

MapWidget.prototype.handleUserActionXYZ = function() {

  // dialog arguments
  var args = {

    // all top-level properties will available as variables in the
    // output object of the dialog 

    foo: "bar" // will be available as <<foo>> in the dialog tiddler

    // special dialog parameters
    dialog: {

      // will be available as field values in the dialog
      // this is important when working with forms and fields
      // need to be preselected
      preselects: {
        foo1: "bar2" // will be available via {{!!foo1}}
      }
    }
  };

  // the dialog name refers to a tiddler template
  // isConfirmed defines whether the user pressed "ok"
  // outTObj the output of the dialog with all its results
  this.dialogManager.open("dialog_template123", args, function(isConfirmed, outTObj) {

    // this is a callback function that is invoked when the dialog closed

    if(!isConfirmed) return; // user cancelled the dialog? then abort.

    console.log(outTObj.fields.foo1); 

  });
}

How the dialog template "dialog_template123" looks like

title: $:/plugins/felixhayashi/tiddlymap/dialog/dialog_template123
subtitle: {{$:/core/images/info-button }} Dialog Title
buttons: ok_cancel <----- The button-template displayed in the dialog footer;
                          Other button-templates are e.g. "ok_suppress" or "close" etc.

Hello <<foo>>!

{{!!foo1}}

… form with various input elements …

All changes of the dialog are written into a temp tiddler that is later passed to the dialog callback as Tiddler object outTObj

The dialog manager now creates an isolated, temporary dialog environment with the tiddlers:

$:/temp/tmap/dialog-b4386c9b-0fd5-4534-93c1-34d412f29250        // the dialog instance
$:/temp/tmap/dialog-b4386c9b-0fd5-4534-93c1-34d412f29250/output // the dialog output
$:/temp/tmap/dialog-b4386c9b-0fd5-4534-93c1-34d412f29250/result // the closing trigger
$:/temp/tmap/dialog-b4386c9b-0fd5-4534-93c1-34d412f29250/temp   // a temp store for temporary dialog stuff

When the user presses a footer button, the $:/temp/tmap/dialog-b4386c9b-0fd5-4534-93c1-34d412f29250/result tiddler gets its text set to "1" if confirmed or "0" if cancelled and the change listener of the DialogManager that listened for tiddler changes of this tiddler now knows that the user closed the dialog and invokes the callback javascript function with the output object and the boolean variable isConfirmed

The footer of the dialog that holds the dialog buttons looks like this:

title: $:/plugins/felixhayashi/tiddlymap/dialogFooter/ok_cancel

<$transclude tiddler="$:/plugins/felixhayashi/tiddlymap/dialogFooter/ok" mode="inline" />
<$button class="tmap-dialog-button tmap-cancel-button" tooltip="Close dialog without saving">Cancel
  <!-- trigger dialog callback -->
  <$action-setfield $tiddler=<<result>> text="" />
</$button>

In fact, the dialog system of TiddlyMap is even a bit more complex but these are the main parts.

-Felix

00SS commented 5 years ago

I know this is an old Issue, but can't there be a non-overwritable X button on the top right of the modal by default to close the tiddler? This is in case the default button in the tiddler's footer field is overwitten and for some reason does not close the modal. See the last line of Issue #3842.

Jermolene commented 5 years ago

Hi @00SS I wouldn't be against adding a permanent close button to modals.

00SS commented 5 years ago

I think an X button on the top right edge of the title area where the subtitle field displays, the default action is simply tm-close-tiddler

BUT perhaps @felixhayashi can suggest a good way of doing this that has some method of addressing the concern to know HOW the modal was closed, in his case to know it is a cancel.

00SS commented 5 years ago

Issue came up in Google Groups: No Close Button on Popup dialogues "modals" and TiddlyMap?

I installed tiddlymap to an existing wiki 5.1.19 However all the modals that popup do not have a close button

felixhayashi commented 5 years ago

BUT perhaps @felixhayashi can suggest a good way of doing this that has some method of addressing the concern to know HOW the modal was closed, in his case to know it is a cancel.

Hi,

As described earlier in this thread, the modal would have to notify the process that opened the modal so it can react on a closing event. Everything else is, as said already, highly problematic.

Solution 1)

$tw.rootWidget.dispatchEvent({ type: 'tm-modal', ... }) is an async action which returns a promise that resolves on close.

Solution 2)

$tw.rootWidget.dispatchEvent({
  type: 'tm-modal',
  paramObject: { twModalStateTiddler: "some_name_provided_by_caller" }
)

The twModalStateTiddler property (the property name shoud be tw reserved) is an optional temporary (data) tiddler name passed as param to the event which will have its text field updated as something happens to the modal, e.g. it will be status: open if it is not closed and status: closed otherwise. This way it works like the normal TW event queue and the caller of the modal can react to it.

00SS commented 5 years ago

@felixhayashi thanks for replying. Which of the two methods would you suggest is used? Which would be easier to implement?

pmario commented 3 years ago

@Jermolene IMO with the combination of tm-modal, the keyboard-widget and the event-catcher-widget all of the OP should be doable

IMO can be closed

Jermolene commented 3 years ago

Thanks @pmario