diff --git a/CHANGELOG.md b/CHANGELOG.md index ad7bd1d..3946e73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added +* Sidebar component. + ### Changed ### Fixed diff --git a/app/components/flowbite/sidebar.rb b/app/components/flowbite/sidebar.rb new file mode 100644 index 0000000..298c79f --- /dev/null +++ b/app/components/flowbite/sidebar.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module Flowbite + # Renders a fixed-position sidebar container. + # + # Use {Flowbite::Sidebar} as the outer shell and + # {Flowbite::Sidebar::Navigation} inside it to render the list of + # {Flowbite::Sidebar::Item}s. + # + # @example Usage + # <%= render(Flowbite::Sidebar.new) do %> + # <%= render(Flowbite::Sidebar::Navigation.new) do |nav| %> + # <% nav.with_item do %> + # <%= render(Flowbite::Sidebar::Item.new(href: "/dashboard")) { "Dashboard" } %> + # <% end %> + # <% end %> + # <% end %> + # + # @see https://flowbite.com/docs/components/sidebar/ + # @lookbook_embed SidebarPreview + class Sidebar < ViewComponent::Base + class << self + def classes + [ + "fixed", "top-0", "left-0", "z-40", "w-64", "h-screen", + "transition-transform", "-translate-x-full", "sm:translate-x-0" + ] + end + end + + # @param class [Array] Additional CSS classes for the sidebar + # container. + # @param options [Hash] Additional HTML options for the sidebar container. + def initialize(class: nil, **options) + super() + @class = Array.wrap(binding.local_variable_get(:class)) + @options = options + end + + def call + content_tag(:aside, aside_options) do + content_tag(:div, class: wrapper_classes) do + content + end + end + end + + private + + def aside_classes + self.class.classes + @class + end + + def aside_options + {class: aside_classes, "aria-label": "Sidebar"}.merge(@options) + end + + def wrapper_classes + ["h-full", "px-3", "py-4", "overflow-y-auto", "bg-neutral-primary-soft"] + end + end +end diff --git a/app/components/flowbite/sidebar/item.rb b/app/components/flowbite/sidebar/item.rb new file mode 100644 index 0000000..5372648 --- /dev/null +++ b/app/components/flowbite/sidebar/item.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module Flowbite + class Sidebar + # Renders a sidebar navigation item. + # + # Each item renders as a list item containing a link. Optionally, an icon + # can be provided using the +icon+ slot, which will be displayed before the + # label text. + # + # @example Basic item + # <%= render Flowbite::Sidebar::Item.new(href: "/dashboard") { "Dashboard" } %> + # + # @example Item with icon + # <%= render(Flowbite::Sidebar::Item.new(href: "/dashboard")) do |item| %> + # <% item.with_icon do %> + # ... + # <% end %> + # Dashboard + # <% end %> + # + # @viewcomponent_slot icon An optional icon displayed before the label text. + class Item < ViewComponent::Base + renders_one :icon + + attr_reader :href, :options + + class << self + def classes + [ + "flex", "items-center", "px-2", "py-1.5", "text-body", + "rounded-base", "hover:bg-neutral-tertiary", "hover:text-fg-brand", "group" + ] + end + end + + # @param class [Array] Additional CSS classes for the link element. + # @param href [String] The URL for the navigation link. + # @param options [Hash] Additional HTML attributes for the link element. + def initialize(href:, class: nil, **options) + super() + @class = Array.wrap(binding.local_variable_get(:class)) + @href = href + @options = options + end + + def call + content_tag(:li) do + link_options = {class: link_classes}.merge(options) + content_tag(:a, href: href, **link_options) do + concat(icon) if icon? + concat(content_tag(:span, content, class: label_classes)) + end + end + end + + private + + def label_classes + "ms-3" if icon? + end + + def link_classes + self.class.classes + @class + end + end + end +end diff --git a/app/components/flowbite/sidebar/navigation.rb b/app/components/flowbite/sidebar/navigation.rb new file mode 100644 index 0000000..5b15721 --- /dev/null +++ b/app/components/flowbite/sidebar/navigation.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module Flowbite + class Sidebar + # Renders the navigation list for a sidebar. + # + # This component renders a +