Is CoreComponents a prelude of an official Phoenix components library?

Hi all,
just out of curiosity:

  • Is the CoreComponents module a prelude of an official Phoenix components library? Or are they just a great example of defining function components to be reused throughout our application.
  • Is there any point in the Phoenix roadmap to add an official set of components?

PS:

  • do you think that open sourcing components is a nice idea (if they don’t really have a specific feature that makes them stand out)?

I am asking because my fear is that a user could be baffled by having to choose between a pletora of similar component libraries (looking at you linux distros!)

I would love to hear your thoughts.

Thank you

I don’t think that components would be officially changed a lot. Phoenix core team prefers Tailwind and they are most probably not going to add support for let’s say Bootstrap or other CSS framework/library. Also the Tailwind styles are prepared for a default look of Phoenix applications.

It would be amazing if somebody would write a library that defines a set of components (i.e. HEEX + live part) and gives a user not only choice which framework/library to use, but also which theme should be applied.

import Config

config :my_app, MyApp.ComponentManager, default_theme: :theme_name, style: :bootstrap
defmodule MyApp.ComponentManager do
  use MyLib.ComponentManager, otp_app: :my_app

  component :custom_component, MyApp.Components.CustomComponent

  style :bootstrap, MyLib.Styles.Bootstrap
  # …

  theme :theme_name, MyApp.Themes.ThemeName
  # …

  manage config, %{profile: profile} do
    %{
      theme: profile.preferred_theme || config[:default_theme],
      # …
    }
  end

  prepare  _config, _component_module, data do
    data
  end
end
defimpl MyApp.Themes.ThemeName, for: MyLib.ComponentTheme.Button do
  def dark_variant(opts) do
    # …
  end

  def light_variant(opts) do
    # …
  end
end
defimpl MyLib.Styles.Bootstrap for: MyLib.Component.Button do
  def basic_style(opts) do
    type = opts[:type] || :primary
    # …
  end

  def render(assigns) do
    ~H"""
    <!-- … -->
    """
  end

  def sizing_style(opts) do
    # …
  end

  # …
end
alias MyApp.ComponentManager
<ComponentManager.button phx-click="…" profile={@current_user.profile} type: :primary>
  Button text …
</ComponentManager.button>
4 Likes

They try to be that while containing all the components needed to support the variuous generators phoenix ships with. They’re meant to be flexible enough to be used outside of that, but they primarily serve that usecase.

1 Like

There is a direct support for themes in the alpha3 version of Bootstrap 5.3

I took some time this weekend to translate core_components.ex to BS5, but it does not really behave like Tailwind. The modal needs to be different, but the rest is just changing markup and css classes.

1 Like

Did you just changed markup and styles or also added also a live support i.e. did you rewrite the JavaScript part as well?

I build my own assets pipeline. I started with --no-tailwind, --no-esbuild.

Then added webpack and esbuild-loader… Speed of esbuild, and webpack config :slight_smile:

Bootstrap 5 comes with JQuery free javascript. By defining right classes, bootstrap js takes control…

1 Like

For example… the modal

  def modal(assigns) do
    assigns = assign_new(assigns, :patch, fn -> nil end)

    ~H"""
    <div id="modal" class="phx-modal fade-in" phx-remove={hide_modal()}>
      <div
        id="modal-content"
        class="phx-modal-content fade-in-scale"
        phx-click-away={JS.dispatch("click", to: "#close")}
        phx-window-keydown={JS.dispatch("click", to: "#close")}
        phx-key="escape"
      >
        <%= if @patch do %>
          <%= live_patch "✖", to: @patch, id: "close", class: "phx-modal-close", phx_click: hide_modal() %>
        <% else %>
         <a id="close" href="#" class="phx-modal-close" phx-click={hide_modal()}>✖</a>
        <% end %>

        <%= render_slot(@inner_block) %>
      </div>
    </div>
    """
  end

and the corresponding js code, where I changed transition classes

  defp hide_modal(js \\ %JS{}) do
    js
    |> JS.hide(to: "#modal", transition: "fade-out")
    |> JS.hide(to: "#modal-content", transition: "fade-out-scale")
  end
2 Likes

There is no reason to make the components module be a config option for your application. It’s just a module you can import… If you want component modules that respond to some styling options, just define a a macro that generates the right AST and generate that AST inside a module. Otherwise if you want some kind of runtime configuration just make your components get those configuration values at runtime.

I’ve been working on this too. It’s available here: GitHub - tmbb/bootstrap5_components: Phoenix components using the bootstrap CSS framework (it used a GitHub dependency which hasn’t bee published on Hex). Not that there are some spacing issues I’m still trying to solve (I’m not by all means a CSS pro) and that Icon handling isn’t great yet (I’m still writing inline CSS in the webpage)

You basically use it like this:

defmodule MyAppWeb.CoreComponents do
  use CodeGen,
    module: Bootstrap5Components
end

You use my CodeGen module instead of just using Bootstrap5Components directly because CodeGen contains some utilities to dump the source code of the functions defined by Bootstrap5Components so that they can be customized. That way you have the benefits of code generators without the added bloat of the code you don’t want to change.

1 Like