WIP (pre-alpha) Ash UI Extension & Component Lib
Hey all. π
I'm happy to tease the near public release of
Plegethon
(provisional title), a (very experimental) Ash UI extension and LiveView
component library!
It's inspired by Petal
, AshAdmin
, and AshAuthenticationPhoenix
. I'm trying very hard to design in the ability to configure/extend nearly every aspect of the components, and to have an Ash-core level of escape hatches/overrides in the extension config. The idea is that you can declare UI config in your Ash resource, then use that in "smart components" that know how to introspect the Ash resource to build themselves. Kind of like AshPhoenix.Form
w/ auto: true
, but for the UI (examples in images).
The override system for components is very similar to AshAuthenticationPhoenix
, if you've used that. It adds some other goodies like taking in a color_theme
map, and using that to generate a json
colors file for Tailwind/Tails and even custom themed (light & dark) Makeup
css. π
The components go from whole-page level down to smaller things like forms, filter forms for queries, and datatables. Forms support groups & nesting, and I'm currently working on a wizard syntax. Still working on finishing up form relationships & autocomplete components, that should be ready soon. I have an awesome datatable component in another project, I just need to port it over to this lib.
I've been working for quite some time on this, and I'm coming close to releasing this publicly under MIT. I'm interested in opening limited access to some brave souls that want to dig in and help get this to the beta phase. Particularly those are somewhat familiar with writing Ash extensions and/or .heex
components and willing to submit PRs. This really isn't ready to be used unless you want to get your hands dirty -- lots of stuff is missing!
If you're interested in jumping in, let me know via a DM and I'll consider giving you GitHub access within the next few days. π


56 Replies
^ One thing to note in the default overrides image is that the class overrides are functions that are passed all the component's assigns, so you can get very specific! There is a
merge_classes
helper that also runs them through Tails.classes
, passing in the components class props as well. This means your custom overrides can still be reliably overridden by props without weird Tailwind conflicts. πVery very excited about this!!! Will absolutely test this out with AshHq and could maybe even see rewriting ash admin with these standardized components!
Did a little cleanup on the override system, and I think I'm pretty happy with it! As an example:
The
class_for
macro looks up the override for the module, and figures out how to apply the override (whether it's a function or simple binary/list). So you can also do e.g.:
It then assumes that it should append the attribute with the same key as the override to the end of the class list for runtime overrides, but it also accepts a different key (or list of keys) to get a different override value from the assigns. For example:
This really cleans up the code a lot, ensures classes always run through Tails.classes
and makes it easier to grep what's going on!
As an added bonus, it makes it trivial to make your own components that leverage all that tooling. Just add use Phlegethon.Components.Base
and set your overrides. π
And one more thing: Many of the basic components have the same API as the Phoenix 1.7
core_components
, so you can probably swap out core_components
for Phlegethon.Components
, and have it work out of the box w/ Phoenix generated LiveViews.That sounds awesome!
Wow. This is next level. I will use it. A component library that combines resources and tails is the ash way.. but might need something else like Flowbite or headlessUI
Love the look of this, awesome work! I had to google Phlegethon (turns out I'm not up on Greek mythology). 'River of fire', long may Ash inspire all the names
Yeah, every once in a while I have to do something to justify spending 5 semesters learning Greek. π
I have been hard at work grinding out more of the essential stuff. Did a lot of refinement on the override system.
For one thing, the overrides are set at compile time via application config, so given
config.exs
:
The component is able to access them at compile time, allowing validation in attributes:
And a public function is also provided, allowing runtime access as well:
This took quite a bit of refactoring, so I'm glad I haven't opened it just yet as that would have been painful for everyone else to endure. πAnother thing I'm quite proud of is I think I came up w/ some great improvements to the Phoenix Flash message component. I made this component a little smarter in that it can accept JSON encoded strings, and it extracts some info from them, such as
ttl
(time-to-live for auto-closing flashes) and also a title. This allows for some really nice flash messages. This is all done w/ a hook so it's snappy on the client-side, and it also intelligently resets the ttl
if the content of the flash has changed, so you don't get a quick-close if you overwrite the same flash type. All of this is configurable, of course.
To use the JSON flash, there's a simple encoder function:


Putting a lot of effort into generating good docs. π

Well, after a long day/night and a lot of caffeine, I added an
override
prop to Phlegethon.Component
(by extending/rewriting half of Phoenix.Component.Declarative
). π₯
So a few notes about the behavior/options:
- All overrides get merged in as the default value
- required: true
option will raise an error if no overrides provide a value -- at compile time! π
- values
option can be either an atom, which will pull in overrides for it, or a list, which cannot be overridden
- All types can be either the type itself, or an arrity 1 function that accepts assigns and returns the type itself
- Adds the :class
type, which additionally handles passing in assigns and running if it's a function, and runs it through Tails.classes
Edit: hit enter too early, lolπ₯ π₯ π₯
That looks really awesome
gimme gimme gimme π
HalfLife 3 soon. π
I uhh... learned a lot about macros doing this, lol
A demo:
And when corrected, notice the
default
and values
extraction:
Question: can child components be overriden with this system? Or non-simple values like functions? Wondering if we could use this to get some of the flexibility we want in ash_authentication_phoenix
Since you essentially modeled it after what @jart did there
(but it seems have spent much more time on that part of the pattern)
Just wondering if there is some synergy that can be achieved with this.
- Basically if you define an override prop, the logic for generic types is this:
prop || override || nil
.
- For CSS types, it ends up like this: Tails.classes(override || nil, prop)
.
- If you mark an override prop as required
, it will throw an error if you don't have an override defined in some included override file
- ALL override types can be functions, in which case they will be passed the assigns:
The assigns get merged in this order: normal defaults -> prop assigns -> override functions -> class type override functions.
One big difference from the AshAuthenticationPhoenix override system is that it does not support passing in runtime overrides, just overriding via props.
In practice, I'm not sure if that matters or not.
But I needed compile-time override definitions to make components behave the way I wanted.oh, interesting.
Also, at this point, I don't think there is any shared implementation code. It's more "inspired by" at this point, lol
Makes sense. I think there would likely still be some potential synergy there
I'm a bit biased, but I think this could make quite a splash. In Phoenix-land, even outside Ash. π
I think you are very much correct π May also be a big draw for people to use Ash if its got stuff to integrate in fancy ways
Is the stuff like smart form in its own separate dependency? Or is it all one package?
ATM, Ash is a hard dependency, but that's just because I haven't looked up how to do optional dependencies/optional compilation. Should be no reason why it would need Ash, and I have the smart components in separate modules that could optionally compile.
Honestly, its pretty much just what it sounds like
I plan to put it all in the same package, but to have optional deps.
you mark the dep as optional, and then wrap those modules in
if Code.ensure_loaded?(Ash) do
Cool. π
How about testing?
Well, in your test/dev environments the dep is always present
Marking it as optional only count for
:prod
env?I don't know the best way to test without it though
yeah, exactly
Awesome.
You're using spark for the DSLs right?
Ah, I guess you aren't
because you've got top level things
π’
For the Ash extension? Yeah. Not for the overrides, though.
Would it make sense to?
If we supported top level builders, yeah
but alas, we do not
Ahh, right.
I'd add it though, just for you
Well, it's pretty simple anyway. Would be no reason not to swap later, I've already done all the work for it at this point, lol
You can take a look soon, open to any feedback on it,.
Once I finish this refactor of the core components w/ my new API, I'll give you access even though it's still a bit of a mess in a lot of places. π
I'd add it though, just for youHe really wants to add this he keeps asking me if I want it
lol
I just don't want the lack of it to stop people from using it
nah.
...Will you add it and refactor to use it? π
I'm going to wind up with a DSL like:
it's fine
lol, probably not
I could see Ash using it for the basic resource things like:
Thinking it over, perhaps using Spark would be great even if the only benefit I got from it was not having to fight w/ the formatter on re-adding parenthesis every time I have a compile error. lol
Now youβre getting it.
For those interested, the repo is now public! https://github.com/frankdugan3/phlegethon
There is a lot to work on and lots of stuff half-finished, but I feel like the component API and docs are pretty stable. Here is a list of the top priorities I will be working on this week:
- Finish up the
Core
components (replacement of core_components.ex
- <.modal> needs polish
- <.table> needs polish
- Flesh out the <.smart_form>
component
- Port my <.smart_data_table>
component
- Trim out all the deprecated cruft that doesn't need to be in info.ex
(ignore that file for now) and finish re-working the tests to work in the lib (they were from a different app originally)
- Add in the ability to do multi-step forms in the extension and <.smart_form>
component (aka wizard)
- Add a nice autocomplete/multiselect component, and make it ash-smart for use in forms w/ relationship managementGitHub
GitHub - frankdugan3/phlegethon: An Ash user interface extension wi...
An Ash user interface extension with smart Phoenix components. - GitHub - frankdugan3/phlegethon: An Ash user interface extension with smart Phoenix components.
To clarify the current state, it's not quite usable as a component lib just yet, so this is more of a thing for those interested in development and checking out the
overridable
component API, and the :class
type. I think it makes for a very composable and clean experience. If you follow the simple dev instructions in the readme, you can check out the component previewer and documentation for yourself. :ashley:I did try and put some effort into decent docs out of the gate, in particular the override modules and components are self-documenting (it extends Phoenix.Component), and any functions used in overridables will link to the appropriate override module that defines them. It should be very easy to click right through any part of a component or override module and find the source right away.





And FWIW, the project is already almost 8K LoC! No wonder it's been taking me so long to get it out the door. π
Been hard at work over the past couple weeks!
The core components is just about "ready" to be used after a significant refactor. I no longer hack Phoenix.Component.Declarative (too much maintenance), and instead use a macro to add the overrides at runtime. There are still quite a few compile time checks and optimizations to ensure things work without much head-scratching.
One example is fuzzy-matching on attribute names when it can't match up attribute with override.
Going for Elm-level errors. π
Nicely done
Found a nice way to cleanly extend
Phoenix.Component.attr/3
, so this is the final(?) component override API:
Has compile-time checks to catch any missing calls/incompatible options. The overridable attrs are accumulated and assigned (in order of definition) by assign_overridables/1
.
All of the components and documentation have been updated to the new new API. Been dog-fooding it and it's starting to feel pretty good, though I may be biased. π



Oh, and the default style is a dark/light variant on the one Phoenix gives you out of the box.


Just pushed up the basic version of an autocomplete component, along with support for simple relationships in
SmartForm
.
For simple use:
There are lots of props/overrides to configure lots of behavior, including a slot for custom templates for the options.
To use an autocomplete for simple relationships in Ash w/ a SmartForm
:
It makes use of hooks and Phoenix.JS
so that all the things like moving selection, intercepting key events etc. are all done client-slide for a snappy feel.
It should also be relatively accessible, but I'm no expert on that. π€
There's still a lot more to add, such as loading indicators, supporting multiple entries, supporting more complex relationship types, and allowing templates for adding new entries on the fly. And of course all the bugs that will naturally pop up. Will be adding to it bit-by-bit!Would it be possible to create a channel for phlegethon or do you want questions/discussion in github issues?
Where do you stand on this @frankdugan3 ? Should I make a phlegethon channel or a maple ui channel π
Hmm... I think stick w/ phlegethon channel for now. I don't want to change it twice, and I haven't made up my mind on maple ui as a name yet. π
#pyro
I don't mind questions/discussion on either, but if there is an existing issue on the topic, would be better to continue it on the issue to keep it all in one place.
Maple is easier than phegle.. <checks notes>
I posted my findings in #pyro which I'm quickly learning how to spell.