Skip to content
Merged
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
2 changes: 2 additions & 0 deletions docs/app/components/shared/components_list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def components
{name: "Avatar", path: docs_avatar_path},
{name: "Badge", path: docs_badge_path},
{name: "Breadcrumb", path: docs_breadcrumb_path},
{name: "Bubble", path: docs_bubble_path},
{name: "Button", path: docs_button_path},
{name: "Calendar", path: docs_calendar_path},
{name: "Card", path: docs_card_path},
Expand All @@ -34,6 +35,7 @@ def components
{name: "Input", path: docs_input_path},
{name: "Link", path: docs_link_path},
{name: "Masked Input", path: masked_input_path},
{name: "Message", path: docs_message_path},
{name: "Pagination", path: docs_pagination_path},
{name: "Popover", path: docs_popover_path},
{name: "Progress", path: docs_progress_path},
Expand Down
8 changes: 8 additions & 0 deletions docs/app/controllers/docs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ def breadcrumb
render Views::Docs::Breadcrumb.new
end

def bubble
render Views::Docs::Bubble.new
end

def button
render Views::Docs::Button.new
end
Expand Down Expand Up @@ -162,6 +166,10 @@ def masked_input
render Views::Docs::MaskedInput.new
end

def message
render Views::Docs::Message.new
end

def pagination
render Views::Docs::Pagination.new
end
Expand Down
2 changes: 2 additions & 0 deletions docs/app/lib/site_files.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class SiteFiles
{title: "Avatar", path: "/docs/avatar", description: "Image and fallback primitives for representing a user."},
{title: "Badge", path: "/docs/badge", description: "Small status or label element."},
{title: "Breadcrumb", path: "/docs/breadcrumb", description: "Navigation trail showing the current location in a hierarchy."},
{title: "Bubble", path: "/docs/bubble", description: "Chat bubble surface with variants, alignment, grouping, and reactions."},
{title: "Button", path: "/docs/button", description: "Button component and button-like variants."},
{title: "Calendar", path: "/docs/calendar", description: "Date field component for entering and editing dates."},
{title: "Card", path: "/docs/card", description: "Content container with header, content, and footer primitives."},
Expand All @@ -107,6 +108,7 @@ class SiteFiles
{title: "Input", path: "/docs/input", description: "Styled input field primitive."},
{title: "Link", path: "/docs/link", description: "Link component with button-like and underline variants."},
{title: "Masked Input", path: "/docs/masked_input", description: "Form input with an applied mask."},
{title: "Message", path: "/docs/message", description: "Chat message layout pairing an avatar with bubbles, headers, and footers."},
{title: "Pagination", path: "/docs/pagination", description: "Page navigation with next and previous links."},
{title: "Popover", path: "/docs/popover", description: "Triggered rich content panel."},
{title: "Progress", path: "/docs/progress", description: "Progress bar for task completion state."},
Expand Down
167 changes: 167 additions & 0 deletions docs/app/views/docs/bubble.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# frozen_string_literal: true

class Views::Docs::Bubble < Views::Base
def view_template
component = "Bubble"

div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
render Docs::Header.new(title: "Bubble", description: "A chat bubble surface for displaying conversational content, with variants, alignment, grouping, and reactions.")

Heading(level: 2) { "Usage" }

render Docs::VisualCodeExample.new(title: "Default", context: self) do
<<~RUBY
Bubble(align: :end) do
BubbleContent { "Hey there! what's up?" }
end
RUBY
end

render Docs::VisualCodeExample.new(title: "Conversation", context: self) do
<<~RUBY
div(class: "flex flex-col gap-8") do
Bubble(align: :end) do
BubbleContent { "Hey there! what's up?" }
end
BubbleGroup do
Bubble(variant: :muted) do
BubbleContent { "Hey! Want to see chat bubbles?" }
end
Bubble(variant: :muted) do
BubbleContent { "I can group messages, switch sides, and keep the whole thread easy to scan." }
BubbleReactions(role: "img", aria_label: "Reaction: thumbs up") do
span { "👍" }
end
end
end
Bubble(align: :end) do
BubbleContent { "Sure. Hit me with your best demo." }
end
end
RUBY
end

Heading(level: 2) { "Variants" }

render Docs::VisualCodeExample.new(title: "Variants", context: self) do
<<~RUBY
div(class: "flex flex-col gap-4 w-full") do
Bubble(variant: :default) { BubbleContent { "Default" } }
Bubble(variant: :secondary) { BubbleContent { "Secondary" } }
Bubble(variant: :muted) { BubbleContent { "Muted" } }
Bubble(variant: :tinted) { BubbleContent { "Tinted" } }
Bubble(variant: :outline) { BubbleContent { "Outline" } }
Bubble(variant: :ghost) { BubbleContent { "Ghost — unframed, full width for assistant text or markdown." } }
Bubble(variant: :destructive) { BubbleContent { "Destructive — something went wrong." } }
end
RUBY
end

Heading(level: 2) { "Alignment" }

render Docs::VisualCodeExample.new(title: "Start and end", context: self) do
<<~RUBY
div(class: "flex flex-col gap-4 w-full") do
Bubble(align: :start, variant: :muted) do
BubbleContent { "Aligned to the start (receiver)." }
end
Bubble(align: :end) do
BubbleContent { "Aligned to the end (sender)." }
end
end
RUBY
end

Heading(level: 2) { "Reactions" }

render Docs::VisualCodeExample.new(title: "Reactions", context: self) do
<<~RUBY
div(class: "flex flex-col gap-10 w-full py-6") do
Bubble(variant: :muted) do
BubbleContent { "Reactions anchor to the bubble edge." }
BubbleReactions(role: "img", aria_label: "Reactions: thumbs up, fire, eyes, and 2 more") do
span { "👍" }
span { "🔥" }
span { "👀" }
span { "+2" }
end
end
Bubble(align: :end) do
BubbleContent { "Place them on top and to the start too." }
BubbleReactions(side: :top, align: :start, role: "img", aria_label: "Reaction: heart") do
span { "❤️" }
end
end
end
RUBY
end

Heading(level: 2) { "Group" }

render Docs::VisualCodeExample.new(title: "Bubble group", context: self) do
<<~RUBY
BubbleGroup do
Bubble(variant: :muted) { BubbleContent { "First message in the group." } }
Bubble(variant: :muted) { BubbleContent { "Second one, tighter spacing." } }
Bubble(variant: :muted) { BubbleContent { "Third, all stacked together." } }
end
RUBY
end

Heading(level: 2) { "Link or button bubble" }

render Docs::VisualCodeExample.new(title: "Interactive content", context: self) do
<<~RUBY
div(class: "flex flex-col gap-4 w-full") do
Bubble(align: :end) do
BubbleContent(as: :a, href: "#") { "Tap to open the link →" }
end
Bubble(variant: :outline) do
BubbleContent(as: :button, type: "button") { "Retry sending" }
end
end
RUBY
end

Heading(level: 2) { "With Tooltip" }

render Docs::VisualCodeExample.new(title: "Reveal metadata on hover", context: self) do
<<~RUBY
Tooltip do
TooltipTrigger(class: "w-fit") do
Bubble(variant: :muted, class: "max-w-none") do
BubbleContent { "Read 9:41 AM" }
end
end
TooltipContent do
Text { "Delivered and read" }
end
end
RUBY
end

Heading(level: 2) { "With Popover" }

render Docs::VisualCodeExample.new(title: "Surface details on demand", context: self) do
<<~RUBY
Popover do
PopoverTrigger do
Bubble(variant: :destructive, class: "max-w-none") do
BubbleContent { "Message failed to send" }
end
end
PopoverContent(class: "w-64") do
Text(weight: :semibold) { "Delivery error" }
Text(size: :sm, class: "text-muted-foreground") { "The recipient's inbox is full. Try again later." }
end
end
RUBY
end

render Components::ComponentSetup::Tabs.new(component_name: component)

# components
render Docs::ComponentsTable.new(component_files(component))
end
end
end
173 changes: 173 additions & 0 deletions docs/app/views/docs/message.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# frozen_string_literal: true

class Views::Docs::Message < Views::Base
def view_template
component = "Message"

div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
render Docs::Header.new(title: "Message", description: "A chat message layout that pairs an avatar with bubbles, headers, and footers. Built on top of Avatar and Bubble.")

Heading(level: 2) { "Usage" }

render Docs::VisualCodeExample.new(title: "Conversation", context: self) do
<<~RUBY
div(class: "flex flex-col gap-6") do
Message(align: :end) do
MessageAvatar do
Avatar(size: :sm) do
AvatarImage(src: "https://github.com/joeldrapper.png", alt: "@me")
AvatarFallback { "ME" }
end
end
MessageContent do
Bubble do
BubbleContent { "Deploying to prod real quick." }
end
end
end

Message do
MessageAvatar do
Avatar(size: :sm) do
AvatarImage(src: "https://github.com/shadcn.png", alt: "@rabbit")
AvatarFallback { "R" }
end
end
MessageContent do
Bubble(variant: :muted) do
BubbleContent { "It's 4:55 PM. On a Friday." }
end
end
end

Message(align: :end) do
MessageAvatar do
Avatar(size: :sm) do
AvatarImage(src: "https://github.com/joeldrapper.png", alt: "@me")
AvatarFallback { "ME" }
end
end
MessageContent do
Bubble do
BubbleContent { "It's a one-line change." }
end
MessageFooter { "Delivered" }
end
end

Message do
MessageAvatar do
Avatar(size: :sm) do
AvatarImage(src: "https://github.com/shadcn.png", alt: "@rabbit")
AvatarFallback { "R" }
end
end
MessageContent do
BubbleGroup do
Bubble(variant: :muted) do
BubbleContent { "It's always a one-line change 😭." }
end
Bubble(variant: :muted) do
BubbleContent { "Alright, let me take a look." }
BubbleReactions(role: "img", aria_label: "Reaction: thumbs up") do
span { "👍" }
end
end
end
end
end
end
RUBY
end

Heading(level: 2) { "With header" }

render Docs::VisualCodeExample.new(title: "Header and footer", context: self) do
<<~RUBY
Message do
MessageAvatar do
Avatar(size: :sm) do
AvatarImage(src: "https://github.com/shadcn.png", alt: "@oliver")
AvatarFallback { "OL" }
end
end
MessageContent do
MessageHeader { "Oliver" }
Bubble(variant: :muted) do
BubbleContent { "Pushed the fix, can you review?" }
end
MessageFooter { "9:41 AM" }
end
end
RUBY
end

Heading(level: 2) { "Alignment" }

render Docs::VisualCodeExample.new(title: "Sender and receiver", context: self) do
<<~RUBY
div(class: "flex flex-col gap-6") do
Message do
MessageAvatar do
Avatar(size: :sm) do
AvatarImage(src: "https://github.com/shadcn.png", alt: "@rabbit")
AvatarFallback { "R" }
end
end
MessageContent do
Bubble(variant: :muted) { BubbleContent { "Aligned to the start." } }
end
end
Message(align: :end) do
MessageAvatar do
Avatar(size: :sm) do
AvatarImage(src: "https://github.com/joeldrapper.png", alt: "@me")
AvatarFallback { "ME" }
end
end
MessageContent do
Bubble { BubbleContent { "Aligned to the end." } }
end
end
end
RUBY
end

Heading(level: 2) { "Group" }

render Docs::VisualCodeExample.new(title: "Message group", context: self) do
<<~RUBY
MessageGroup do
Message do
MessageAvatar do
Avatar(size: :sm) do
AvatarImage(src: "https://github.com/shadcn.png", alt: "@rabbit")
AvatarFallback { "R" }
end
end
MessageContent do
Bubble(variant: :muted) { BubbleContent { "First message." } }
end
end
Message do
MessageAvatar do
Avatar(size: :sm) do
AvatarImage(src: "https://github.com/shadcn.png", alt: "@rabbit")
AvatarFallback { "R" }
end
end
MessageContent do
Bubble(variant: :muted) { BubbleContent { "Second, tighter spacing." } }
end
end
end
RUBY
end

render Components::ComponentSetup::Tabs.new(component_name: component)

# components
render Docs::ComponentsTable.new(component_files(component))
end
end
end
Loading