Giter Site home page Giter Site logo

chrisgreg / bloom Goto Github PK

View Code? Open in Web Editor NEW
299.0 6.0 13.0 4.32 MB

The opinionated extension to Phoenix core_components

Home Page: https://bloom-ui.fly.dev

License: MIT License

Elixir 81.94% Dockerfile 1.44% CSS 0.27% JavaScript 3.46% HTML 12.66% Shell 0.20% Batchfile 0.03%
component-generator component-library phoenix phoenix-liveview tailwindcss

bloom's Introduction

Bloom Logo

Bloom

Hex.pm Downloads Hex.pm Version Hex.pm License

The opinionated extension to Phoenix core_components, inspired by shadcn.

A set of HEEX components that can be independently installed and edited to your hearts content.

Working both with Live and dead controller views, written in HEEX using TailwindCSS and designed to be bolted onto applications using Phoenix Core Components.

Demo

Visit the demo site to see a Phoenix Storybook of Bloom components.

Installation

Can be installed by adding bloom to your list of dependencies in mix.exs:

def deps do
  [
    {:bloom, "~> 0.0.8"}
  ]
end

Relies on Phoenix being installed.

Installing components

All components can be installed by running the following mix command in your project root

mix bloom.install <component_name>

Some components require Tailwind Config changes - refer to the component doc for more information.

View all components by running:

mix bloom.install help

Install Ecto-backed waitlist & landing page by running:

mix bloom.landing_page

You will need to run mix ecto.gen.migration to create the migration and copy and migrate the contents from waitlist.ex to complete.

You will also need to add the generated LiveView to your router.

Frequently Asked Questions

Why are the components manually installed?

So you can customise them to your hearts content and make them your own easily. The source code of the components will live in your project so you can tweak them as you see fit.

The colours aren't showing up!

Tailwind treeshakes colours that aren't in use at compile time - this means dynamic ones set by Phoenix Components won't show if the colours aren't already part of the page. You can circumvent this by safelisting colours in your tailwind.config.js or ensuring the colours are already in use (not recommended).

See the Tailwind website for more information.

You can safelist by Regex or manually.

Recommended safelist:

  safelist: [
    {
      pattern: /bg-+/,
      variants: ["focus", "hover", "group-hover", "active"],
    },
    {
      pattern: /text-+/,
      variants: ["focus", "hover", "group-hover", "active"],
    },
    {
      pattern: /from-+/,
      variants: ["focus", "hover", "group-hover", "active"],
    },
    {
      pattern: /to-+/,
      variants: ["focus", "hover", "group-hover", "active"],
    },
    {
      pattern: /inset-+/,
      variants: ["focus", "hover", "group-hover", "active"],
    },
    {
      pattern: /opacity-+/,
      variants: ["focus", "hover", "group-hover", "active"],
    },
    {
      pattern: /transition-+/,
      variants: ["focus", "hover", "group-hover", "active"],
    },
  ],

There aren't many components

I'm gonna be adding components slowly but this repo welcomes contributions for beautiful, useful components not already covered by the excellent core_components that ship with Phoenix.

How do I preview these components?

I'm going to be adding a Storybook and website soon.

What needs work?

Everything!

This isn't a complete package yet - hence the version.

  • Plenty of work to be done around the developer experience extracting the docs and making the installation of components even easier.
  • Ways of collocating JS in an easy way until LiveView 1.0.
  • Making the components better, mobile friendly, dark/light themes, standardising props
  • Adding loads more components
  • Reporting which Tailwind classes to safelist after publishing
  • Turning this and the demo site into an umbrella application with git subdirectories

Contribution

  • Create your component in lib/bloom/components
  • Adhere to Phoenix component standards
  • Ensure any Tailwind config changes are documented in the @moduledoc
  • Run mix bloom.generate_templates when ready to submit
  • Increase semantic versioning for new publish
  • Raise pull request
  • Add Storybook story to the bloom-ui site to demo component

bloom's People

Contributors

c-sinclair avatar chrisgreg avatar latachz avatar samjowen avatar twistingtwists avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

bloom's Issues

component request: combo-box

Would love to have a combo-box. Tbh for my purposes right now even something like Petal's single select would do the job (i.e. I don't need the checkboxes embedded in the select dropdown).

Proposal: layout component

Hi,

The layout component should implement at least the stack and the sidebar layouts, ref: https://every-layout.dev
The cover would be an excellent addition.

Petal's approach to structure is a decent start https://docs.petal.build/petal-pro-documentation/fundamentals/layouts-and-menus

Here is what I did to port the sidebar layout from the https://github.com/themesberg/flowbite-astro-admin-dashboard/tree/main (it also implements the stack) into one of my experiments.

  1. The root layout didn't change, but I removed all classes from the main tag
  <.flash_group flash={@flash} />
  <%= @inner_content %>
</main>
  1. Added components/astro.ex
defmodule SesameWeb.AstroComponents do
  use Phoenix.Component
  import SesameWeb.AstroComponents.NavBarSidebar
  import SesameWeb.AstroComponents.Sidebar

  attr :main_menu_items, :list
  attr :user_menu_items, :list
  attr :user, :map
  attr :current_page, :atom, required: true
  slot(:inner_block)

  def layout_sidebar(assigns) do
    assigns =
      assigns
      |> assign_new(:main_menu_items, fn -> SesameWeb.Menus.main_menu_items(assigns[:user]) end)
      |> assign_new(:user_menu_items, fn -> SesameWeb.Menus.user_menu_items(assigns[:user]) end)

    ~H"""
    <.navbar_sidebar user={@user} user_menu_items={@user_menu_items} />
    <.sidebar main_menu_items={@main_menu_items} current_page={@current_page} />

    <div class="flex pt-16 overflow-hidden  dark:bg-gray-900">
      <div
        id="main-content"
        class="relative w-full h-full overflow-y-auto  lg:ml-64 dark:bg-gray-900 min-h-[calc(100vh-64px)]"
      >
        <%= render_slot(@inner_block) %>
      </div>
    </div>
    """
  end
end

I don't remember much now, but astro/sidebar.ex and astro/navbar_sidebar.ex basically loop over the menu items and render them accordingly:

defmodule SesameWeb.AstroComponents.Sidebar do
  use Phoenix.Component
  use SesameWeb, :verified_routes
  import SesameWeb.CoreComponents, only: [icon: 1]

  attr :main_menu_items, :list, required: true
  attr :current_page, :atom, required: true

  def sidebar(assigns) do
    ~H"""
    <aside
      id="sidebar"
      class="fixed top-0 left-0 z-20 flex flex-col flex-shrink-0 hidden w-64 h-full pt-16 font-normal duration-75 lg:flex transition-width"
      aria-label="Sidebar"
      phx-hook="SideBar"
    >
      <div class="relative flex flex-col flex-1 min-h-0 pt-0 bg-white border-r border-gray-200 dark:bg-gray-800 dark:border-gray-700">
        <div class="flex flex-col flex-1 pt-5 pb-28 overflow-y-auto scrollbar scrollbar-w-2 scrollbar-thumb-rounded-[0.1667rem] scrollbar-thumb-slate-200 scrollbar-track-gray-400 dark:scrollbar-thumb-slate-900 dark:scrollbar-track-gray-800">
          <div class="flex-1 px-3 space-y-1 bg-white divide-y divide-gray-200 dark:bg-gray-800 dark:divide-gray-700">
            <ul class="pb-2 space-y-2">
              <%= for menu_item <- @main_menu_items do %>
                <li>
                  <.link class={main_menu_item_class(@current_page, menu_item.name)} navigate={menu_item.path}>
                    <%= if is_binary(menu_item.icon) do %>
                      <.icon
                        name={"hero-#{menu_item.icon}"}
                        class="w-6 h-6 text-gray-500 transition duration-75 group-hover:text-gray-900 dark:text-gray-400 dark:group-hover:text-white"
                      />
                    <% end %>

                    <span class="ml-3" sidebar-toggle-item><%= menu_item.label %></span>
                  </.link>
                </li>
              <% end %>
            </ul>
          </div>
        </div>
      </div>
    </aside>

    <div class="fixed inset-0 z-10 hidden bg-gray-900/50 dark:bg-gray-900/90" id="sidebarBackdrop"></div>
    """
  end

  def main_menu_item_class(current_page, current_page) do
    main_menu_item_class_base_class() <> " bg-gray-100 dark:bg-gray-700"
  end

  def main_menu_item_class(_current_page, _page) do
    main_menu_item_class_base_class()
  end

  def main_menu_item_class_base_class() do
    "flex items-center p-2 text-base text-gray-900 rounded-lg hover:bg-gray-100 group dark:text-gray-200 dark:hover:bg-gray-700"
  end
end
defmodule SesameWeb.AstroComponents.NavBarSidebar do
  use Phoenix.Component
  import SesameWeb.CoreComponents, only: [icon: 1]
  import PetalComponents.Dropdown, only: [dropdown_menu_item: 1]

  attr :user, :map, required: true
  attr :user_menu_items, :list, required: true

  def navbar_sidebar(assigns) do
    ~H"""
    <nav class="fixed z-30 w-full bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700">
      <div class="px-3 py-3 lg:px-5 lg:pl-3">
        <div class="flex items-center justify-between">
          <div class="flex items-center justify-start">
            <button
              id="toggleSidebarMobile"
              aria-expanded="true"
              aria-controls="sidebar"
              class="p-2 text-gray-600 rounded cursor-pointer lg:hidden hover:text-gray-900 hover:bg-gray-100 focus:bg-gray-100 dark:focus:bg-gray-700 focus:ring-2 focus:ring-gray-100 dark:focus:ring-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white"
            >
              <.icon id="toggleSidebarMobileHamburger" class="w-6 h-6" name="hero-bars-3" />
              <.icon id="toggleSidebarMobileClose" class="hidden w-6 h-6" name="hero-x-mark" />
            </button>
            <a href="/" class="flex ml-2 md:mr-24">
              <%!-- <img src="images/logo.svg" class="h-8 mr-3" alt="FlowBite Logo" /> --%>
              <span class="self-center text-xl  font-black sm:text-2xl whitespace-nowrap dark:text-white">
                🏔️Sesame
              </span>
            </a>

            <%!-- <SearchInput /> --%>
          </div>

          <div class="flex items-center">
            <.notifications />
            <.apps />

            <%!-- <ColorModeSwitcher /> --%>
            <!-- Profile -->
            <.user_menu user={@user} user_menu_items={@user_menu_items} />
          </div>
        </div>
      </div>
    </nav>
    """
  end

  defp user_menu(assigns) do
    ~H"""
    <div class="flex items-center ml-3">
      <div>
        <button
          type="button"
          class="flex text-sm bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
          id="user-menu-button-2"
          aria-expanded="false"
          data-dropdown-toggle="dropdown-2"
        >
          <span class="sr-only">Open user menu</span>
          <%!-- <img
            class="w-8 h-8 rounded-full"
            src="https://flowbite.com/docs/images/people/profile-picture-5.jpg"
            alt="user photo"
          /> --%>

          <.icon class="w-8 h-8 bg-stone-50" name="hero-user-circle-solid" />
        </button>
      </div>
      <!-- Dropdown menu -->
      <div
        class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600"
        id="dropdown-2"
      >
        <div class="px-4 py-3" role="none">
          <p class="text-sm text-gray-900 dark:text-white" role="none">
            <%= @user.name %>
          </p>
          <p class="text-sm font-medium text-gray-900 truncate dark:text-gray-300" role="none">
            <%= @user.email %>
          </p>
        </div>
        <ul class="py-1" role="none">
   
          <%= for menu_item <-  @user_menu_items do %>
            <li>
              <.dropdown_menu_item
                link_type={if menu_item[:method], do: "a", else: "live_redirect"}
                method={if menu_item[:method], do: menu_item[:method], else: nil}
                to={menu_item.path}
                class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white"
              >
                <%= if is_binary(menu_item.icon) do %>
                  <.icon name={"hero-#{menu_item.icon}"} class="w-5 h-5 text-gray-500 dark:text-gray-400" />
                <% end %>

                <%= menu_item.label %>
              </.dropdown_menu_item>
            </li>
          <% end %>
        </ul>
      </div>
    </div>
    """
  end
end

Then, I was able to choose which layout I wanted to use on a LiveView basis:

<.layout_sidebar user={@current_user} current_page={:participants}>
   Content
</.layout_sidebar>

The SesameWeb.Menus contains definitions of menus as described in Petal docs https://docs.petal.build/petal-pro-documentation/fundamentals/layouts-and-menus#menus

defmodule SesameWeb.Menus do

  use SesameWeb, :verified_routes

  # Public menu 
  def public_menu_items(_user \\ nil),
    do: [
      %{label: "Features", path: "/#features"},
    ]

  # Signed out main menu
  def main_menu_items(nil) do 
    []
  end

  # Signed in main menu
  def main_menu_items(current_user) do
     build_menu([:my_notifications, :participants], current_user)
  end
end

Hope this helps!

List not rendering properly on Mobile

Screenshot_20240505-054638.png

Don't know enough to guess why this is happening but the second list element here is definitely not rendering correctly for me on Android Chrome.

Proposal: Ensure that code is formatted before publish CI

Hi @chrisgreg , seeing as this is a repository that shares common goals with shadcn's library — code ownership for the users, I think it would be a good idea to ensure that the code in this repository is always formatted before publishing.

Therefore, I propose adding a CI step that looks something like this as part of the build or publish job:

      # Step: Check that the checked in code has already been formatted.
      # This step fails if something was found unformatted.
      - name: Check Formatting
        run: mix format --check-formatted

What do you think?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.