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
25 changes: 25 additions & 0 deletions app/components/shared/container.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

module Components
module Shared
class Container < Components::Base
DEFAULT_CLASS = "container mx-auto w-full px-4"
SIZE_CLASSES = {
sm: "max-w-md",
md: "max-w-2xl",
lg: "max-w-4xl",
xl: "max-w-6xl",
"2xl": "max-w-7xl"
}

def initialize(size: "md", **attrs)
@attrs = attrs
@attrs[:class] = [DEFAULT_CLASS, SIZE_CLASSES[size].to_s, @attrs[:class]]
end

def view_template(&)
div(**@attrs, &)
end
end
end
end
90 changes: 90 additions & 0 deletions app/components/shared/flash.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# frozen_string_literal: true

module Components
module Shared
class Flash < Components::Base
# STYLE_CLASS = {
# notice: 'bg-primary text-primary-foreground',
# alert: 'bg-destructive text-destructive-foreground'
# }
# TRANSITION_CLASS = "transition ease-in-out data-[state=open]:animate-in data-[state=open]:fade-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom"

def initialize(variant: :notice, title: nil, description: nil)
@variant = variant.to_sym
@description = description
@title = title
end

def view_template(&block)
li(
role: "status",
aria_live: "off",
aria_atomic: "true",
tabindex: "0",
data_state: "open",
data_controller: "dismissable",
# data_swipe_direction: "right",
class: [
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md p-4 pr-6 pt-3.5 shadow-lg transition-all data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full border",
("bg-background text-foreground" if notice?),
("destructive group border-destructive bg-destructive text-destructive-foreground" if alert?)
],
style: "user-select:none; touch-action:none"
) do
div(class: "grid gap-1") do
if @title
div(class: "text-sm font-semibold [&+div]:text-xs") { @title }
div(class: "text-sm opacity-90") { @description }
else
div { @description }
end
end
block&.call
close_button # sits at top right of toast
end
end

private

def close_button
button(
type: "button",
class: [
"absolute right-1 top-1 rounded-md p-1 opacity-0 transition-opacity focus:opacity-100 focus:outline-none focus:ring-1 focus:ring-ring group-hover:opacity-100",
("text-foreground/50 hover:text-foreground" if notice?),
("text-destructive-foreground/50 hover:text-destructive-foreground focus:ring-destructive-foreground focus:ring-offset-destructive-foreground" if alert?)
],

data: {
action: "click->dismissable#dismiss"
}
) do
x_icon
end
end

def x_icon
svg(
width: "15",
height: "15",
viewbox: "0 0 15 15",
fill: "none",
xmlns: "http://www.w3.org/2000/svg",
class: "h-4 w-4"
) do |s|
s.path(
d:
"M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z",
fill: "currentColor",
fill_rule: "evenodd",
clip_rule: "evenodd"
)
end
end

def alert? = @variant == :alert

def notice? = @variant == :notice
end
end
end
23 changes: 23 additions & 0 deletions app/components/shared/flashes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module Components
module Shared
class Flashes < Components::Base
def initialize(notice: nil, alert: nil)
@notice = notice
@alert = alert
end

def view_template(&block)
ol(
tabindex: "-1",
class:
"pointer-events-none fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px] gap-y-1"
) do
render Shared::Flash.new(variant: :notice, description: @notice) if @notice
render Shared::Flash.new(variant: :alert, description: @alert) if @alert
end
end
end
end
end
44 changes: 44 additions & 0 deletions app/components/shared/grid_pattern.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

module Components
module Shared
class GridPattern < Components::Base
def initialize(spacing: :md)
sizes = {
xs: 15,
sm: 25,
md: 50,
lg: 100,
xl: 200
}

@spacing = sizes[spacing]
end

def view_template
svg(
class:
"absolute inset-0 -z-10 h-[calc(100vh_/_2)] w-full stroke-border [mask-image:radial-gradient(100%_100%_at_top_right,white,transparent)]",
aria_hidden: "true"
) do |s|
s.defs do
s.pattern(
id: "0787a7c5-978c-4f66-83c7-11c213f99cb7",
width: @spacing,
height: @spacing,
x: "50%",
y: "-1",
patternunits: "userSpaceOnUse"
) { s.path(d: "M.5 200V.5H200", fill: "none") }
end
s.rect(
width: "100%",
height: "100%",
stroke_width: "0",
fill: "url(#0787a7c5-978c-4f66-83c7-11c213f99cb7)"
)
end
end
end
end
end
24 changes: 24 additions & 0 deletions app/components/shared/head.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module Components
module Shared
class Head < Components::Base
include Phlex::Rails::Layout

def view_template
head do
title { "RubyUI - Component Library" }
meta name: "viewport", content: "width=device-width,initial-scale=1"
meta name: "turbo-refresh-method", content: "morph"
meta name: "turbo-refresh-scroll", content: "preserve"
meta name: "view-transition", content: "same-origin"
csp_meta_tag
csrf_meta_tags
stylesheet_link_tag "https://api.fontshare.com/v2/css?f[]=general-sans@1&display=swap", data_turbo_track: "reload"
stylesheet_link_tag "application", data_turbo_track: "reload"
javascript_include_tag "application", data_turbo_track: "reload", type: "module"
end
end
end
end
end
18 changes: 18 additions & 0 deletions app/components/shared/logo.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module Components
module Shared
class Logo < Components::Base
def view_template
a(href: root_url, class: "mr-6 flex items-center space-x-2") do
Heading(level: 4, class: "flex items-center") {
img(src: image_url("logo.svg"), class: "h-4 block dark:hidden")
img(src: image_url("logo_dark.svg"), class: "h-4 hidden dark:block")
span(class: "sr-only") { "RubyUI" }
Badge(variant: :amber, size: :sm, class: "ml-2 whitespace-nowrap") { "Pre Release" }
}
end
end
end
end
end
141 changes: 141 additions & 0 deletions app/components/shared/menu.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# frozen_string_literal: true

module Components
module Shared
class Menu < Components::Base
def view_template
div(class: "pb-4") do
# Main routes (Docs, Components, Themes, Github, Discord, Twitter)
div(class: "md:hidden") do
main_link("Docs", docs_introduction_path)
main_link("Components", docs_accordion_path)
main_link("Themes", theme_path("default"))
main_link("Github", "https://github.com/PhlexUI/phlex_ui")
main_link("Discord", ENV["DISCORD_INVITE_LINK"])
main_link("Twitter", ENV["PHLEXUI_TWITTER_LINK"])
end

# GETTING STARTED
h4(class: "mb-1 mt-4 rounded-md px-2 py-1 text-sm font-semibold") { "Getting Started" }
div(class: "grid grid-flow-row auto-rows-max text-sm") do
getting_started_links.each do |getting_started|
menu_link(getting_started)
end
end

# INSTALLATION
h4(class: "mb-1 mt-4 rounded-md px-2 py-1 text-sm font-semibold") { "Installation" }
div(class: "grid grid-flow-row auto-rows-max text-sm") do
installation_links.each do |installation|
menu_link(installation)
end
end

# COMPONENTS
h4(class: "mb-1 mt-4 rounded-md px-2 py-1 text-sm font-semibold flex items-center gap-x-2") do
plain "Components"
Badge(variant: :primary, size: :sm) { components.count.to_s }
end
div(class: "grid grid-flow-row auto-rows-max text-sm") do
components.each do |component|
menu_link(component)
end
end
end
end

def getting_started_links
[
{name: "Introduction", path: docs_introduction_path},
{name: "Installation", path: docs_installation_path},
{name: "Dark mode", path: docs_dark_mode_path},
{name: "Theming", path: docs_theming_path},
{name: "Customizing components", path: docs_customizing_components_path}
]
end

def installation_links
[
{name: "Rails - JS Bundler", path: docs_installation_rails_bundler_path},
{name: "Rails - Importmaps", path: docs_installation_rails_importmaps_path}
]
end

def components
[
{name: "Accordion", path: docs_accordion_path},
{name: "Alert", path: docs_alert_path},
{name: "Alert Dialog", path: docs_alert_dialog_path},
{name: "Aspect Ratio", path: docs_aspect_ratio_path},
{name: "Avatar", path: docs_avatar_path},
{name: "Badge", path: docs_badge_path},
{name: "Breadcrumb", path: docs_breadcrumb_path, badge: "New"},
{name: "Button", path: docs_button_path},
{name: "Calendar", path: docs_calendar_path},
{name: "Card", path: docs_card_path},
# { name: "Chart", path: docs_chart_path, badge: "New" },
{name: "Checkbox", path: docs_checkbox_path},
{name: "Checkbox Group", path: docs_checkbox_group_path},
{name: "Clipboard", path: docs_clipboard_path},
{name: "Codeblock", path: docs_codeblock_path},
{name: "Collapsible", path: docs_collapsible_path},
{name: "Combobox", path: docs_combobox_path, badge: "Updated"},
{name: "Command", path: docs_command_path},
{name: "Context Menu", path: docs_context_menu_path},
{name: "Date Picker", path: docs_date_picker_path},
{name: "Dialog / Modal", path: docs_dialog_path},
{name: "Dropdown Menu", path: docs_dropdown_menu_path},
{name: "Form", path: docs_form_path},
{name: "Hover Card", path: docs_hover_card_path},
{name: "Input", path: docs_input_path},
{name: "Link", path: docs_link_path},
{name: "Masked Input", path: masked_input_path},
{name: "Pagination", path: docs_pagination_path, badge: "New"},
{name: "Popover", path: docs_popover_path},
{name: "Progress", path: docs_progress_path, badge: "New"},
{name: "Radio Button", path: docs_radio_button_path, badge: "New"},
{name: "Select", path: docs_select_path, badge: "New"},
{name: "Sheet", path: docs_sheet_path},
{name: "Shortcut Key", path: docs_shortcut_key_path},
{name: "Skeleton", path: docs_skeleton_path, badge: "New"},
{name: "Switch", path: docs_switch_path},
{name: "Table", path: docs_table_path},
{name: "Tabs", path: docs_tabs_path},
{name: "Textarea", path: docs_textarea_path},
{name: "Theme Toggle", path: docs_theme_toggle_path},
{name: "Tooltip", path: docs_tooltip_path},
{name: "Typography", path: docs_typography_path}
]
end

def menu_link(component)
current_path = component[:path] == request.path
a(
href: component[:path],
class: [
"group flex w-full items-center rounded-md border border-transparent px-2 py-1 hover:underline",
(current_path ? "text-foreground font-medium" : "text-muted-foreground")
]
) do
span(class: "flex items-center gap-x-1") do
span { component[:name] }
Badge(variant: :success, size: :sm, class: "ml-1") { component[:badge] } if component[:badge]
end
end
end

def main_link(name, path)
current_path = path == request.path
a(
href: path,
class: [
"group flex w-full items-center rounded-md border border-transparent px-2 py-1 hover:underline",
(current_path ? "text-foreground font-medium" : "text-muted-foreground")
]
) do
name
end
end
end
end
end
Loading