Skip to content

Add sidebar component #186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions app/components/docs/visual_code_example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ def self.reset_collected_code
@@collected_code = []
end

def initialize(title: nil, description: nil, context: nil)
def initialize(title: nil, description: nil, src: nil, context: nil)
@title = title
@description = description
@src = src
@context = context
end

Expand Down Expand Up @@ -43,7 +44,7 @@ def view_template(&)
def render_header
div do
if @title
div(class: "flex items-center gap-x-2 mb-1") do
div(class: "") do
Components.Heading(level: 4) { @title.capitalize }
end
end
Expand Down Expand Up @@ -71,6 +72,20 @@ def render_tab_contents(&)
end

def render_preview_tab(&block)
return iframe_preview if @src

raw_preview
end

def iframe_preview
div(class: "relative aspect-[4/2.5] w-full overflow-hidden rounded-md border") do
div(class: "absolute inset-0 hidden w-[1600px] bg-background md:block") do
iframe(src: @src, class: "size-full")
end
end
end

def raw_preview
div(class: "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 relative rounded-md border") do
div(class: "preview flex min-h-[350px] w-full justify-center p-10 items-center") do
decoded_code = CGI.unescapeHTML(@display_code)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module RubyUI
class DropdownMenuContent < Base
def view_template(&block)
div(data: {ruby_ui__dropdown_menu_target: "content"}, class: "hidden", style: "width: max-content; position: absolute; top: 0; left: 0;") do
div(data: {ruby_ui__dropdown_menu_target: "content"}, class: "hidden", style: "width: max-content; position: absolute; top: 0; left: 0; z-index: 50;") do
div(**attrs, &block)
end
end
Expand Down
99 changes: 99 additions & 0 deletions app/components/ruby_ui/sidebar/collapsiable_sidebar.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# frozen_string_literal: true

module RubyUI
class CollapsiableSidebar < Base
def initialize(side: :left, variant: :sidebar, collapsible: :offcanvas, open: true, **attrs)
@side = side
@variant = variant
@collapsible = collapsible
@open = open
super(**attrs)
end

def view_template(&)
MobileSidebar(side: @side, **attrs, &)
div(**mix(sidebar_attrs, attrs)) do
div(**gap_element_attrs)
div(**content_wrapper_attrs) do
div(**content_attrs, &)
end
end
end

private

def sidebar_attrs
{
class: "group peer hidden text-sidebar-foreground md:block",
data: {
state: @open ? "expanded" : "collapsed",
collapsible: @open ? "" : @collapsible,
variant: @variant,
side: @side,
collapsible_kind: @collapsible,
ruby_ui__sidebar_target: "sidebar"
}
}
end

def gap_element_attrs
{
class: [
"relative w-[var(--sidebar-width)] bg-transparent transition-[width]",
"duration-200 ease-linear",
"group-data-[collapsible=offcanvas]:w-0",
"group-data-[side=right]:rotate-180",
variant_classes
]
}
end

def content_wrapper_attrs
{
class: [
"fixed inset-y-0 z-10 hidden h-svh w-[var(--sidebar-width)]",
"transition-[left,right,width] duration-200 ease-linear md:flex",
content_wrapper_side_classes,
content_wrapper_variant_classes
]
}
end

def content_attrs
{
class: [
"flex h-full w-full flex-col bg-sidebar",
"group-data-[variant=floating]:rounded-lg",
"group-data-[variant=floating]:border",
"group-data-[variant=floating]:border-sidebar-border",
"group-data-[variant=floating]:shadow"
],
data: {
sidebar: "sidebar"
}
}
end

def variant_classes
if %i[floating inset].include?(@variant)
"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
else
"group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)]"
end
end

def content_wrapper_side_classes
return "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" if @side == :left

"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]"
end

def content_wrapper_variant_classes
if %i[floating inset].include?(@variant)
"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
else
"group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)] group-data-[side=left]:border-r group-data-[side=right]:border-l"
end
end
end
end
99 changes: 99 additions & 0 deletions app/components/ruby_ui/sidebar/collapsible_sidebar.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# frozen_string_literal: true

module RubyUI
class CollapsibleSidebar < Base
def initialize(side: :left, variant: :sidebar, collapsible: :offcanvas, open: true, **attrs)
@side = side
@variant = variant
@collapsible = collapsible
@open = open
super(**attrs)
end

def view_template(&)
MobileSidebar(side: @side, **attrs, &)
div(**mix(sidebar_attrs, attrs)) do
div(**gap_element_attrs)
div(**content_wrapper_attrs) do
div(**content_attrs, &)
end
end
end

private

def sidebar_attrs
{
class: "group peer hidden text-sidebar-foreground md:block",
data: {
state: @open ? "expanded" : "collapsed",
collapsible: @open ? "" : @collapsible,
variant: @variant,
side: @side,
collapsible_kind: @collapsible,
ruby_ui__sidebar_target: "sidebar"
}
}
end

def gap_element_attrs
{
class: [
"relative w-[var(--sidebar-width)] bg-transparent transition-[width]",
"duration-200 ease-linear",
"group-data-[collapsible=offcanvas]:w-0",
"group-data-[side=right]:rotate-180",
variant_classes
]
}
end

def content_wrapper_attrs
{
class: [
"fixed inset-y-0 z-10 hidden h-svh w-[var(--sidebar-width)]",
"transition-[left,right,width] duration-200 ease-linear md:flex",
content_wrapper_side_classes,
content_wrapper_variant_classes
]
}
end

def content_attrs
{
class: [
"flex h-full w-full flex-col bg-sidebar",
"group-data-[variant=floating]:rounded-lg",
"group-data-[variant=floating]:border",
"group-data-[variant=floating]:border-sidebar-border",
"group-data-[variant=floating]:shadow"
],
data: {
sidebar: "sidebar"
}
}
end

def variant_classes
if %i[floating inset].include?(@variant)
"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
else
"group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)]"
end
end

def content_wrapper_side_classes
return "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" if @side == :left

"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]"
end

def content_wrapper_variant_classes
if %i[floating inset].include?(@variant)
"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
else
"group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)] group-data-[side=left]:border-r group-data-[side=right]:border-l"
end
end
end
end
45 changes: 45 additions & 0 deletions app/components/ruby_ui/sidebar/mobile_sidebar.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

module RubyUI
class MobileSidebar < Base
SIDEBAR_WIDTH_MOBILE = "18rem"

def initialize(side: :left, **attrs)
@side = side
super(**attrs)
end

def view_template(&)
Sheet(**attrs) do
SheetContent(
side: @side,
class: "w-[var(--sidebar-width)] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden",
style: {
"--sidebar-width": SIDEBAR_WIDTH_MOBILE
},
data: {
sidebar: "sidebar",
mobile: "true"
}
) do
SheetHeader(class: "sr-only") do
SheetTitle { "Sidebar" }
SheetDescription { "Displays the mobile sidebar." }
end
div(class: "flex h-full w-full flex-col", &)
end
end
end

private

def default_attrs
{
data: {
ruby_ui__sidebar_target: "mobileSidebar",
action: "ruby--ui-sidebar:open->ruby-ui--sheet#open:self"
}
}
end
end
end
17 changes: 17 additions & 0 deletions app/components/ruby_ui/sidebar/non_collapsible_sidebar.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module RubyUI
class NonCollapsibleSidebar < Base
def view_template(&)
div(**attrs, &)
end

private

def default_attrs
{
class: "flex h-full w-[var(--sidebar-width)] flex-col bg-sidebar text-sidebar-foreground"
}
end
end
end
29 changes: 29 additions & 0 deletions app/components/ruby_ui/sidebar/sidebar.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module RubyUI
class Sidebar < Base
SIDES = %i[left right].freeze
VARIANTS = %i[sidebar floating inset].freeze
COLLAPSIBLES = %i[offcanvas icon none].freeze

def initialize(side: :left, variant: :sidebar, collapsible: :offcanvas, open: true, **attrs)
raise ArgumentError, "Invalid side: #{side}." unless SIDES.include?(side.to_sym)
raise ArgumentError "Invalid variant: #{variant}." unless VARIANTS.include?(variant.to_sym)
raise ArgumentError, "Invalid collapsible: #{collapsible}." unless COLLAPSIBLES.include?(collapsible.to_sym)

@side = side.to_sym
@variant = variant.to_sym
@collapsible = collapsible.to_sym
@open = open
super(**attrs)
end

def view_template(&)
if @collapsible == :none
NonCollapsibleSidebar(**attrs, &)
else
CollapsibleSidebar(side: @side, variant: @variant, collapsible: @collapsible, open: @open, **attrs, &)
end
end
end
end
20 changes: 20 additions & 0 deletions app/components/ruby_ui/sidebar/sidebar_content.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module RubyUI
class SidebarContent < Base
def view_template(&)
div(**attrs, &)
end

private

def default_attrs
{
class: "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
data: {
sidebar: "content"
}
}
end
end
end
20 changes: 20 additions & 0 deletions app/components/ruby_ui/sidebar/sidebar_footer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module RubyUI
class SidebarFooter < Base
def view_template(&)
div(**attrs, &)
end

private

def default_attrs
{
class: "flex flex-col gap-2 p-2",
data: {
sidebar: "footer"
}
}
end
end
end
Loading
Loading