I’ll be interested in hearing other people’s solutions to this as well. I ended up doing something very similar to what you described above. I already had a global event manager, so I created a UI manager and created a class of events to request modal dialogs, etc. Most of my dialogs have standalone functionality, though a few require some data to be passed, which I store in the UI manager, and a few are chains such as confirmation dialogs, etc. Then, when I need a dialog, I just request it via an event and I use a struct to specify which dialog island/chain to open as well as pass a little data if needed. This is all from C++, so I already have a struct specifying all of the available dialogs just for convenience. It works well, but right now the implementation is somewhere between hacky and robust. I think the architectural design is fine, but it grew somewhat organically so I need to go back and refactor a few things.
For my next project, I may go full Slate in C++ instead of a mix of C++ and UMG. Gwenn’s Helium Rain UI is gorgeous and is very inspiring.