Skip to content

Commit 44a14bc

Browse files
authored
Merge pull request #89 from substancelab/koppen/sidebar-component
Add Sidebar component
2 parents a4be8c3 + 94cf21d commit 44a14bc

File tree

12 files changed

+448
-44
lines changed

12 files changed

+448
-44
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
77

88
### Added
99

10+
* Sidebar component.
11+
1012
### Changed
1113

1214
### Fixed

app/components/flowbite/sidebar.rb

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# frozen_string_literal: true
2+
3+
module Flowbite
4+
# Renders a fixed-position sidebar container.
5+
#
6+
# Use {Flowbite::Sidebar} as the outer shell and
7+
# {Flowbite::Sidebar::Navigation} inside it to render the list of
8+
# {Flowbite::Sidebar::Item}s.
9+
#
10+
# @example Usage
11+
# <%= render(Flowbite::Sidebar.new) do %>
12+
# <%= render(Flowbite::Sidebar::Navigation.new) do |nav| %>
13+
# <% nav.with_item do %>
14+
# <%= render(Flowbite::Sidebar::Item.new(href: "/dashboard")) { "Dashboard" } %>
15+
# <% end %>
16+
# <% end %>
17+
# <% end %>
18+
#
19+
# @see https://flowbite.com/docs/components/sidebar/
20+
# @lookbook_embed SidebarPreview
21+
class Sidebar < ViewComponent::Base
22+
class << self
23+
def classes
24+
[
25+
"fixed", "top-0", "left-0", "z-40", "w-64", "h-screen",
26+
"transition-transform", "-translate-x-full", "sm:translate-x-0"
27+
]
28+
end
29+
end
30+
31+
# @param class [Array<String>] Additional CSS classes for the sidebar
32+
# container.
33+
# @param options [Hash] Additional HTML options for the sidebar container.
34+
def initialize(class: nil, **options)
35+
super()
36+
@class = Array.wrap(binding.local_variable_get(:class))
37+
@options = options
38+
end
39+
40+
def call
41+
content_tag(:aside, aside_options) do
42+
content_tag(:div, class: wrapper_classes) do
43+
content
44+
end
45+
end
46+
end
47+
48+
private
49+
50+
def aside_classes
51+
self.class.classes + @class
52+
end
53+
54+
def aside_options
55+
{class: aside_classes, "aria-label": "Sidebar"}.merge(@options)
56+
end
57+
58+
def wrapper_classes
59+
["h-full", "px-3", "py-4", "overflow-y-auto", "bg-neutral-primary-soft"]
60+
end
61+
end
62+
end
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# frozen_string_literal: true
2+
3+
module Flowbite
4+
class Sidebar
5+
# Renders a sidebar navigation item.
6+
#
7+
# Each item renders as a list item containing a link. Optionally, an icon
8+
# can be provided using the +icon+ slot, which will be displayed before the
9+
# label text.
10+
#
11+
# @example Basic item
12+
# <%= render Flowbite::Sidebar::Item.new(href: "/dashboard") { "Dashboard" } %>
13+
#
14+
# @example Item with icon
15+
# <%= render(Flowbite::Sidebar::Item.new(href: "/dashboard")) do |item| %>
16+
# <% item.with_icon do %>
17+
# <svg class="w-5 h-5" ...>...</svg>
18+
# <% end %>
19+
# Dashboard
20+
# <% end %>
21+
#
22+
# @viewcomponent_slot icon An optional icon displayed before the label text.
23+
class Item < ViewComponent::Base
24+
renders_one :icon
25+
26+
attr_reader :href, :options
27+
28+
class << self
29+
def classes
30+
[
31+
"flex", "items-center", "px-2", "py-1.5", "text-body",
32+
"rounded-base", "hover:bg-neutral-tertiary", "hover:text-fg-brand", "group"
33+
]
34+
end
35+
end
36+
37+
# @param class [Array<String>] Additional CSS classes for the link element.
38+
# @param href [String] The URL for the navigation link.
39+
# @param options [Hash] Additional HTML attributes for the link element.
40+
def initialize(href:, class: nil, **options)
41+
super()
42+
@class = Array.wrap(binding.local_variable_get(:class))
43+
@href = href
44+
@options = options
45+
end
46+
47+
def call
48+
content_tag(:li) do
49+
link_options = {class: link_classes}.merge(options)
50+
content_tag(:a, href: href, **link_options) do
51+
concat(icon) if icon?
52+
concat(content_tag(:span, content, class: label_classes))
53+
end
54+
end
55+
end
56+
57+
private
58+
59+
def label_classes
60+
"ms-3" if icon?
61+
end
62+
63+
def link_classes
64+
self.class.classes + @class
65+
end
66+
end
67+
end
68+
end
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# frozen_string_literal: true
2+
3+
module Flowbite
4+
class Sidebar
5+
# Renders the navigation list for a sidebar.
6+
#
7+
# This component renders a +<ul>+ with navigation items. It can be used
8+
# inside a {Flowbite::Sidebar} for a fixed-position sidebar, or standalone
9+
# in any layout that needs sidebar-style navigation.
10+
#
11+
# @example Inside a Sidebar
12+
# <%= render(Flowbite::Sidebar.new) do %>
13+
# <%= render(Flowbite::Sidebar::Navigation.new) do |nav| %>
14+
# <% nav.with_item do %>
15+
# <%= render(Flowbite::Sidebar::Item.new(href: "/")) { "Home" } %>
16+
# <% end %>
17+
# <% end %>
18+
# <% end %>
19+
#
20+
# @example Standalone
21+
# <%= render(Flowbite::Sidebar::Navigation.new) do |nav| %>
22+
# <% nav.with_item do %>
23+
# <%= render(Flowbite::Sidebar::Item.new(href: "/")) { "Home" } %>
24+
# <% end %>
25+
# <% end %>
26+
class Navigation < ViewComponent::Base
27+
renders_many :items
28+
29+
class << self
30+
def classes
31+
["space-y-2", "font-medium"]
32+
end
33+
end
34+
35+
# @param class [Array<String>] Additional CSS classes for the list element.
36+
# @param options [Hash] Additional HTML options for the list element.
37+
def initialize(class: nil, **options)
38+
super()
39+
@class = Array.wrap(binding.local_variable_get(:class))
40+
@options = options
41+
end
42+
43+
def call
44+
content_tag(:ul, list_options) do
45+
items.each do |item|
46+
concat(item)
47+
end
48+
end
49+
end
50+
51+
private
52+
53+
def list_classes
54+
self.class.classes + @class
55+
end
56+
57+
def list_options
58+
{class: list_classes}.merge(@options)
59+
end
60+
end
61+
end
62+
end

demo/.yardoc/checksums

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ app/components/flowbite/style.rb ef063360cc99cd7a6b8e67a7693326bb5dfb0e42
55
app/components/flowbite/toast.rb 6b822405dd55d87d56979e6cfba55e8f73965047
66
app/components/flowbite/button.rb 6ae7681d3b842d73aa99cddfa5a9b107ede7fea4
77
app/components/flowbite/styles.rb 929c42e428ba5a8e16efacaae0f35380e2f5f95c
8+
app/components/flowbite/sidebar.rb 85033b602a098f3334b9b3e180239ef20a1b6f90
89
app/components/flowbite/input/url.rb f1046824f9b06c8df8e0f567979321b82baac6fa
910
app/components/flowbite/breadcrumb.rb c69ffb465b6e7f2489d4ac9a928e08bdf252fe99
1011
app/components/flowbite/card/title.rb 8067aa1e027c725896b063b67364aecfbf2f7d4e
@@ -19,6 +20,7 @@ app/components/flowbite/input/phone.rb 0dfe3e9a83c4fb9f558405a20601649c7b08922a
1920
app/components/flowbite/input_field.rb 8ef98ace7d4ccb4f474d3063cf48cc5c83cd8068
2021
app/components/flowbite/input/number.rb a33580788ad91308b85955fdb44d37883329dd4e
2122
app/components/flowbite/input/select.rb 9f1a6406efdda2e29d479117a35c2a924bd888c2
23+
app/components/flowbite/sidebar/item.rb 8dc762357988fdf6e9308de129bd7c89b6700472
2224
app/components/flowbite/button/outline.rb 2829cf352a03c00dd99a56a05181c4e1a6794d18
2325
app/components/flowbite/input/checkbox.rb 500f109206a47997bf2bc0732399297a92005dc0
2426
app/components/flowbite/input/password.rb 39a4c2bb2684a0a310175307bd1bdfd9c99c6cd1
@@ -34,6 +36,7 @@ app/components/flowbite/input_field/phone.rb 6a3da98a2dded98b1c4d14c2419077fc9de
3436
app/components/flowbite/input/radio_button.rb 60ccac6862b459b89f9f3335246a51d83ae4af63
3537
app/components/flowbite/input_field/number.rb 32dce4e2bb586f64229dc78b541700df829068c7
3638
app/components/flowbite/input_field/select.rb 1f9788b5fff4be2b65184e73e739da8fa9de0264
39+
app/components/flowbite/sidebar/navigation.rb 0f79a28928a40c222210352eaad86eb111e3a401
3740
app/components/flowbite/breadcrumb/home_icon.rb bd2e47a31a793a79f20bd792a2a7e2dd8094fc5d
3841
app/components/flowbite/input_field/checkbox.rb 4cbc6f541842838234189b383f813b164a06379b
3942
app/components/flowbite/input_field/password.rb 37e592f55f258cc4b81859d48763b9c0eff12f61

demo/.yardoc/object_types

806 Bytes
Binary file not shown.

demo/.yardoc/objects/root.dat

12.4 KB
Binary file not shown.

demo/Gemfile.lock

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: ../..
33
specs:
4-
flowbite-components (0.2.0)
4+
flowbite-components (0.2.1)
55
view_component (>= 4.0.0)
66

77
GEM
@@ -81,11 +81,11 @@ GEM
8181
addressable (2.8.7)
8282
public_suffix (>= 2.0.2, < 7.0)
8383
base64 (0.3.0)
84-
benchmark (0.4.1)
85-
bigdecimal (3.2.2)
84+
benchmark (0.5.0)
85+
bigdecimal (4.0.1)
8686
builder (3.3.0)
87-
concurrent-ruby (1.3.5)
88-
connection_pool (2.5.3)
87+
concurrent-ruby (1.3.6)
88+
connection_pool (3.0.2)
8989
crass (1.0.6)
9090
css_parser (1.21.1)
9191
addressable
@@ -118,7 +118,7 @@ GEM
118118
herb (0.8.10-x86_64-linux-musl)
119119
htmlbeautifier (1.4.3)
120120
htmlentities (4.3.4)
121-
i18n (1.14.7)
121+
i18n (1.14.8)
122122
concurrent-ruby (~> 1.0)
123123
io-console (0.8.1)
124124
irb (1.15.2)
@@ -155,7 +155,6 @@ GEM
155155
net-smtp
156156
marcel (1.0.4)
157157
mini_mime (1.1.5)
158-
mini_portile2 (2.8.9)
159158
minitest (6.0.1)
160159
prism (~> 1.5)
161160
net-imap (0.5.7)
@@ -257,8 +256,6 @@ GEM
257256
rexml (3.4.4)
258257
rouge (4.5.2)
259258
securerandom (0.4.1)
260-
sqlite3 (2.6.0)
261-
mini_portile2 (~> 2.8.0)
262259
sqlite3 (2.6.0-aarch64-linux-gnu)
263260
sqlite3 (2.6.0-aarch64-linux-musl)
264261
sqlite3 (2.6.0-arm-linux-gnu)
@@ -282,10 +279,11 @@ GEM
282279
timeout (0.4.3)
283280
tzinfo (2.0.6)
284281
concurrent-ruby (~> 1.0)
285-
uri (1.0.4)
282+
uri (1.1.1)
286283
useragent (0.16.11)
287-
view_component (4.0.2)
288-
activesupport (>= 7.1.0, < 8.1)
284+
view_component (4.4.0)
285+
actionview (>= 7.1.0)
286+
activesupport (>= 7.1.0)
289287
concurrent-ruby (~> 1)
290288
websocket-driver (0.7.7)
291289
base64

demo/app/views/docs/components/show.html.erb

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,24 @@
11
<article
22
class="
3-
flex flex-wrap md:flex-nowrap gap-8 w-full items-center
3+
flex flex-wrap md:flex-nowrap gap-8 w-full
44
justify-between mx-auto
55
"
66
>
77
<nav
8+
class="
9+
-ml-2 pr-6 py-4 overflow-y-auto border-e border-default
10+
"
811
id="default-sidebar"
9-
class="flex-none h-full transition-transform -translate-x-full sm:translate-x-0"
1012
aria-label="Sidebar"
1113
>
12-
<div
13-
class="
14-
h-full -ml-2 pr-6 py-4 overflow-y-auto border-e
15-
border-default
16-
"
17-
>
18-
<ul class="space-y-2 font-medium">
19-
<% all_components.each do |component| %>
20-
<li>
21-
<%= link_to component.name, docs_component_path(:id => component.path), class: "flex items-center px-2 py-1.5 text-body rounded-base hover:bg-neutral-tertiary hover:text-fg-brand group" %>
22-
</li>
14+
<%= render(Flowbite::Sidebar::Navigation.new) do |nav| %>
15+
<% all_components.each do |component| %>
16+
<% nav.with_item do %>
17+
<%= render(Flowbite::Sidebar::Item.new(href: docs_component_path(id: component.path)).with_content(component.name)) %>
2318
<% end %>
24-
</ul>
25-
</div>
19+
<% end %>
20+
<% end %>
2621
</nav>
27-
2822
<main class="py-4 flex-1 space-y-8">
2923
<%= render(Docs::Headline::H1.new) { @code_object.path } %>
3024

demo/app/views/docs/pages/show.html.erb

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,24 @@
11
<article
22
class="
3-
flex flex-wrap gap-8 w-full max-w-screen-xl items-center
3+
flex flex-wrap gap-8 w-full max-w-screen-xl
44
justify-between mx-auto
55
"
66
>
77
<nav
8+
class="
9+
-ml-2 pr-6 py-4 overflow-y-auto border-e border-default
10+
"
811
id="default-sidebar"
9-
class="flex-none h-full transition-transform -translate-x-full sm:translate-x-0"
1012
aria-label="Sidebar"
1113
>
12-
<div
13-
class="
14-
h-full -ml-2 pr-6 py-4 overflow-y-auto border-e
15-
border-default
16-
"
17-
>
18-
<ul class="space-y-2 font-medium">
19-
<% all_pages.each do |page| %>
20-
<li>
21-
<%= link_to page.title, docs_page_path(page.slug), class: "flex items-center px-2 py-1.5 text-body rounded-base hover:bg-neutral-tertiary hover:text-fg-brand group" %>
22-
</li>
14+
<%= render(Flowbite::Sidebar::Navigation.new) do |nav| %>
15+
<% all_pages.each do |page| %>
16+
<% nav.with_item do %>
17+
<%= render(Flowbite::Sidebar::Item.new(href: docs_page_path(page.slug)).with_content(page.title)) %>
2318
<% end %>
24-
</ul>
25-
</div>
19+
<% end %>
20+
<% end %>
2621
</nav>
27-
2822
<main class="py-4 flex-1">
2923
<div class="format dark:format-invert">
3024
<h1 class="mb-4 text-4xl tracking-tight font-bold text-gray-900 dark:text-white"><%= @page.title %></h1>

0 commit comments

Comments
 (0)