diff --git a/app/components/shared/container.rb b/app/components/shared/container.rb new file mode 100644 index 00000000..4a7de82e --- /dev/null +++ b/app/components/shared/container.rb @@ -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 diff --git a/app/components/shared/flash.rb b/app/components/shared/flash.rb new file mode 100644 index 00000000..b1e39070 --- /dev/null +++ b/app/components/shared/flash.rb @@ -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 diff --git a/app/components/shared/flashes.rb b/app/components/shared/flashes.rb new file mode 100644 index 00000000..ad586c34 --- /dev/null +++ b/app/components/shared/flashes.rb @@ -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 diff --git a/app/components/shared/grid_pattern.rb b/app/components/shared/grid_pattern.rb new file mode 100644 index 00000000..14166286 --- /dev/null +++ b/app/components/shared/grid_pattern.rb @@ -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 diff --git a/app/components/shared/head.rb b/app/components/shared/head.rb new file mode 100644 index 00000000..64b6e574 --- /dev/null +++ b/app/components/shared/head.rb @@ -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 diff --git a/app/components/shared/logo.rb b/app/components/shared/logo.rb new file mode 100644 index 00000000..1de8d9e2 --- /dev/null +++ b/app/components/shared/logo.rb @@ -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 diff --git a/app/components/shared/menu.rb b/app/components/shared/menu.rb new file mode 100644 index 00000000..9e646bff --- /dev/null +++ b/app/components/shared/menu.rb @@ -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 diff --git a/app/components/shared/mobile_menu.rb b/app/components/shared/mobile_menu.rb new file mode 100644 index 00000000..80e98081 --- /dev/null +++ b/app/components/shared/mobile_menu.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Components + module Shared + class MobileMenu < Components::Base + def initialize(**attributes) + @attributes = attributes + end + + def view_template + Sheet(class: @attributes[:class]) do + SheetTrigger do + Button(variant: :ghost, icon: true) do + menu_icon + end + end + SheetContent(class: "w-[300px]", side: :left) do + div(class: "flex flex-col h-full") do + SheetHeader do + div(class: "pl-2") do + render Shared::Logo.new + end + end + div(class: "flex-grow overflow-y-scroll") do + SheetMiddle do + render Shared::Menu.new + end + end + end + end + end + end + + def menu_icon + svg( + xmlns: "http://www.w3.org/2000/svg", + fill: "none", + viewbox: "0 0 24 24", + stroke_width: "1.5", + stroke: "currentColor", + class: "w-4 h-4" + ) do |s| + s.path( + stroke_linecap: "round", + stroke_linejoin: "round", + d: "M3.75 9h16.5m-16.5 6.75h16.5" + ) + end + end + end + end +end diff --git a/app/components/shared/navbar.rb b/app/components/shared/navbar.rb new file mode 100644 index 00000000..ca08e5bd --- /dev/null +++ b/app/components/shared/navbar.rb @@ -0,0 +1,153 @@ +# frozen_string_literal: true + +module Components + module Shared + class Navbar < Components::Base + def view_template + header(class: "supports-backdrop-blur:bg-background/80 sticky top-0 z-50 w-full border-b bg-background/80 backdrop-blur-2xl backdrop-saturate-200") do + div(class: "px-2 sm:px-4 sm:container flex h-14 items-center justify-between") do + div(class: "mr-4 flex items-center") do + render Shared::MobileMenu.new(class: "md:hidden") + render Shared::Logo.new + + Link(href: docs_introduction_path, variant: :ghost, class: "hidden md:inline-block") { "Docs" } + Link(href: docs_accordion_path, variant: :ghost, class: "hidden md:inline-block") { "Components" } + Link(href: theme_path("default"), variant: :ghost, class: "hidden md:inline-block") { "Themes" } + end + div(class: "flex items-center gap-x-2 md:divide-x") do + div(class: "flex items-center") do + twitter_link + github_link + dark_mode_toggle + end + end + end + end + end + + def dark_mode_toggle + RubyUI.ThemeToggle do |toggle| + toggle.light_mode do + Button(variant: :ghost, icon: true) do + svg( + xmlns: "http://www.w3.org/2000/svg", + viewbox: "0 0 24 24", + fill: "currentColor", + class: "w-5 h-5" + ) do |s| + s.path( + d: + "M12 2.25a.75.75 0 01.75.75v2.25a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM7.5 12a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM18.894 6.166a.75.75 0 00-1.06-1.06l-1.591 1.59a.75.75 0 101.06 1.061l1.591-1.59zM21.75 12a.75.75 0 01-.75.75h-2.25a.75.75 0 010-1.5H21a.75.75 0 01.75.75zM17.834 18.894a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 10-1.061 1.06l1.59 1.591zM12 18a.75.75 0 01.75.75V21a.75.75 0 01-1.5 0v-2.25A.75.75 0 0112 18zM7.758 17.303a.75.75 0 00-1.061-1.06l-1.591 1.59a.75.75 0 001.06 1.061l1.591-1.59zM6 12a.75.75 0 01-.75.75H3a.75.75 0 010-1.5h2.25A.75.75 0 016 12zM6.697 7.757a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 00-1.061 1.06l1.59 1.591z" + ) + end + end + end + + toggle.dark_mode do + Button(variant: :ghost, icon: true) do + svg( + xmlns: "http://www.w3.org/2000/svg", + viewbox: "0 0 24 24", + fill: "currentColor", + class: "w-4 h-4" + ) do |s| + s.path( + fill_rule: "evenodd", + d: + "M9.528 1.718a.75.75 0 01.162.819A8.97 8.97 0 009 6a9 9 0 009 9 8.97 8.97 0 003.463-.69.75.75 0 01.981.98 10.503 10.503 0 01-9.694 6.46c-5.799 0-10.5-4.701-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 01.818.162z", + clip_rule: "evenodd" + ) + end + end + end + end + end + + def twitter_link + Link(href: "https://twitter.com/phlexui", variant: :ghost, icon: true) do + svg( + viewbox: "0 0 20 20", + aria_hidden: "true", + fill: "currentColor", + class: "h-4 w-4" + ) do |s| + s.path( + d: + "M6.29 18.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0 0 20 3.92a8.19 8.19 0 0 1-2.357.646 4.118 4.118 0 0 0 1.804-2.27 8.224 8.224 0 0 1-2.605.996 4.107 4.107 0 0 0-6.993 3.743 11.65 11.65 0 0 1-8.457-4.287 4.106 4.106 0 0 0 1.27 5.477A4.073 4.073 0 0 1 .8 7.713v.052a4.105 4.105 0 0 0 3.292 4.022 4.095 4.095 0 0 1-1.853.07 4.108 4.108 0 0 0 3.834 2.85A8.233 8.233 0 0 1 0 16.407a11.615 11.615 0 0 0 6.29 1.84" + ) + end + end + end + + def github_link + Link(href: "https://github.com/PhlexUI/phlex_ui", variant: :ghost, icon: true) do + github_icon + end + end + + private + + def arrow_right_icon + svg( + xmlns: "http://www.w3.org/2000/svg", + viewbox: "0 0 20 20", + fill: "currentColor", + class: "w-5 h-5 ml-1 -mr-1" + ) do |s| + s.path( + fill_rule: "evenodd", + d: + "M5 10a.75.75 0 01.75-.75h6.638L10.23 7.29a.75.75 0 111.04-1.08l3.5 3.25a.75.75 0 010 1.08l-3.5 3.25a.75.75 0 11-1.04-1.08l2.158-1.96H5.75A.75.75 0 015 10z", + clip_rule: "evenodd" + ) + end + end + + def chevron_down_icon + svg( + xmlns: "http://www.w3.org/2000/svg", + viewbox: "0 0 20 20", + fill: "currentColor", + class: "w-4 h-4 ml-1 -mr-1" + ) do |s| + s.path( + fill_rule: "evenodd", + d: + "M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z", + clip_rule: "evenodd" + ) + end + end + + def github_icon + svg( + viewbox: "0 0 16 16", + class: "w-4 h-4", + fill: "currentColor", + aria_hidden: "true" + ) do |s| + s.path( + d: + "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" + ) + end + end + + def account_icon + svg( + xmlns: "http://www.w3.org/2000/svg", + viewbox: "0 0 24 24", + fill: "currentColor", + class: "w-5 h-5" + ) do |s| + s.path( + fill_rule: "evenodd", + d: + "M18.685 19.097A9.723 9.723 0 0021.75 12c0-5.385-4.365-9.75-9.75-9.75S2.25 6.615 2.25 12a9.723 9.723 0 003.065 7.097A9.716 9.716 0 0012 21.75a9.716 9.716 0 006.685-2.653zm-12.54-1.285A7.486 7.486 0 0112 15a7.486 7.486 0 015.855 2.812A8.224 8.224 0 0112 20.25a8.224 8.224 0 01-5.855-2.438zM15.75 9a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z", + clip_rule: "evenodd" + ) + end + end + end + end +end diff --git a/app/components/shared/sidebar.rb b/app/components/shared/sidebar.rb new file mode 100644 index 00000000..9b072252 --- /dev/null +++ b/app/components/shared/sidebar.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Components + module Shared + class Sidebar < Components::Base + def view_template + aside(class: "fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block") do + div(class: "relative overflow-hidden h-full py-6 pl-8 pr-6 lg:py-8", style: "position:relative;--radix-scroll-area-corner-width:0px;--radix-scroll-area-corner-height:0px") do + div(class: "h-full w-full rounded-[inherit]", style: "overflow: hidden scroll;", data_controller: "sidebar-menu") do + render Shared::Menu.new + end + end + end + end + end + end +end diff --git a/app/views/components/shared/container.rb b/app/views/components/shared/container.rb deleted file mode 100644 index 468b1dd1..00000000 --- a/app/views/components/shared/container.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -class Shared::Container < ApplicationComponent - 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 diff --git a/app/views/components/shared/flash.rb b/app/views/components/shared/flash.rb deleted file mode 100644 index 8b8658b7..00000000 --- a/app/views/components/shared/flash.rb +++ /dev/null @@ -1,86 +0,0 @@ -# frozen_string_literal: true - -class Shared::Flash < ApplicationComponent - # 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 diff --git a/app/views/components/shared/flashes.rb b/app/views/components/shared/flashes.rb deleted file mode 100644 index 5fb18b16..00000000 --- a/app/views/components/shared/flashes.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -class Shared::Flashes < ApplicationComponent - 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 diff --git a/app/views/components/shared/grid_pattern.rb b/app/views/components/shared/grid_pattern.rb deleted file mode 100644 index 2f3b6dac..00000000 --- a/app/views/components/shared/grid_pattern.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -class Shared::GridPattern < ApplicationComponent - 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 diff --git a/app/views/components/shared/head.rb b/app/views/components/shared/head.rb deleted file mode 100644 index 567033fa..00000000 --- a/app/views/components/shared/head.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -class Shared::Head < ApplicationComponent - 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 diff --git a/app/views/components/shared/logo.rb b/app/views/components/shared/logo.rb deleted file mode 100644 index ec1ec63e..00000000 --- a/app/views/components/shared/logo.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -class Shared::Logo < ApplicationComponent - 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 diff --git a/app/views/components/shared/menu.rb b/app/views/components/shared/menu.rb deleted file mode 100644 index 3a04acb3..00000000 --- a/app/views/components/shared/menu.rb +++ /dev/null @@ -1,137 +0,0 @@ -# frozen_string_literal: true - -class Shared::Menu < ApplicationComponent - 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 diff --git a/app/views/components/shared/mobile_menu.rb b/app/views/components/shared/mobile_menu.rb deleted file mode 100644 index 47b2e995..00000000 --- a/app/views/components/shared/mobile_menu.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -class Shared::MobileMenu < ApplicationComponent - def initialize(**attributes) - @attributes = attributes - end - - def view_template - Sheet(class: @attributes[:class]) do - SheetTrigger do - Button(variant: :ghost, icon: true) do - menu_icon - end - end - SheetContent(class: "w-[300px]", side: :left) do - div(class: "flex flex-col h-full") do - SheetHeader do - div(class: "pl-2") do - render Shared::Logo.new - end - end - div(class: "flex-grow overflow-y-scroll") do - SheetMiddle do - render Shared::Menu.new - end - end - end - end - end - end - - def menu_icon - svg( - xmlns: "http://www.w3.org/2000/svg", - fill: "none", - viewbox: "0 0 24 24", - stroke_width: "1.5", - stroke: "currentColor", - class: "w-4 h-4" - ) do |s| - s.path( - stroke_linecap: "round", - stroke_linejoin: "round", - d: "M3.75 9h16.5m-16.5 6.75h16.5" - ) - end - end -end diff --git a/app/views/components/shared/navbar.rb b/app/views/components/shared/navbar.rb deleted file mode 100644 index f1baa58d..00000000 --- a/app/views/components/shared/navbar.rb +++ /dev/null @@ -1,149 +0,0 @@ -# frozen_string_literal: true - -class Shared::Navbar < ApplicationComponent - def view_template - header(class: "supports-backdrop-blur:bg-background/80 sticky top-0 z-50 w-full border-b bg-background/80 backdrop-blur-2xl backdrop-saturate-200") do - div(class: "px-2 sm:px-4 sm:container flex h-14 items-center justify-between") do - div(class: "mr-4 flex items-center") do - render Shared::MobileMenu.new(class: "md:hidden") - render Shared::Logo.new - - Link(href: docs_introduction_path, variant: :ghost, class: "hidden md:inline-block") { "Docs" } - Link(href: docs_accordion_path, variant: :ghost, class: "hidden md:inline-block") { "Components" } - Link(href: theme_path("default"), variant: :ghost, class: "hidden md:inline-block") { "Themes" } - end - div(class: "flex items-center gap-x-2 md:divide-x") do - div(class: "flex items-center") do - twitter_link - github_link - dark_mode_toggle - end - end - end - end - end - - def dark_mode_toggle - RubyUI.ThemeToggle do |toggle| - toggle.light_mode do - Button(variant: :ghost, icon: true) do - svg( - xmlns: "http://www.w3.org/2000/svg", - viewbox: "0 0 24 24", - fill: "currentColor", - class: "w-5 h-5" - ) do |s| - s.path( - d: - "M12 2.25a.75.75 0 01.75.75v2.25a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM7.5 12a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM18.894 6.166a.75.75 0 00-1.06-1.06l-1.591 1.59a.75.75 0 101.06 1.061l1.591-1.59zM21.75 12a.75.75 0 01-.75.75h-2.25a.75.75 0 010-1.5H21a.75.75 0 01.75.75zM17.834 18.894a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 10-1.061 1.06l1.59 1.591zM12 18a.75.75 0 01.75.75V21a.75.75 0 01-1.5 0v-2.25A.75.75 0 0112 18zM7.758 17.303a.75.75 0 00-1.061-1.06l-1.591 1.59a.75.75 0 001.06 1.061l1.591-1.59zM6 12a.75.75 0 01-.75.75H3a.75.75 0 010-1.5h2.25A.75.75 0 016 12zM6.697 7.757a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 00-1.061 1.06l1.59 1.591z" - ) - end - end - end - - toggle.dark_mode do - Button(variant: :ghost, icon: true) do - svg( - xmlns: "http://www.w3.org/2000/svg", - viewbox: "0 0 24 24", - fill: "currentColor", - class: "w-4 h-4" - ) do |s| - s.path( - fill_rule: "evenodd", - d: - "M9.528 1.718a.75.75 0 01.162.819A8.97 8.97 0 009 6a9 9 0 009 9 8.97 8.97 0 003.463-.69.75.75 0 01.981.98 10.503 10.503 0 01-9.694 6.46c-5.799 0-10.5-4.701-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 01.818.162z", - clip_rule: "evenodd" - ) - end - end - end - end - end - - def twitter_link - Link(href: "https://twitter.com/phlexui", variant: :ghost, icon: true) do - svg( - viewbox: "0 0 20 20", - aria_hidden: "true", - fill: "currentColor", - class: "h-4 w-4" - ) do |s| - s.path( - d: - "M6.29 18.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0 0 20 3.92a8.19 8.19 0 0 1-2.357.646 4.118 4.118 0 0 0 1.804-2.27 8.224 8.224 0 0 1-2.605.996 4.107 4.107 0 0 0-6.993 3.743 11.65 11.65 0 0 1-8.457-4.287 4.106 4.106 0 0 0 1.27 5.477A4.073 4.073 0 0 1 .8 7.713v.052a4.105 4.105 0 0 0 3.292 4.022 4.095 4.095 0 0 1-1.853.07 4.108 4.108 0 0 0 3.834 2.85A8.233 8.233 0 0 1 0 16.407a11.615 11.615 0 0 0 6.29 1.84" - ) - end - end - end - - def github_link - Link(href: "https://github.com/PhlexUI/phlex_ui", variant: :ghost, icon: true) do - github_icon - end - end - - private - - def arrow_right_icon - svg( - xmlns: "http://www.w3.org/2000/svg", - viewbox: "0 0 20 20", - fill: "currentColor", - class: "w-5 h-5 ml-1 -mr-1" - ) do |s| - s.path( - fill_rule: "evenodd", - d: - "M5 10a.75.75 0 01.75-.75h6.638L10.23 7.29a.75.75 0 111.04-1.08l3.5 3.25a.75.75 0 010 1.08l-3.5 3.25a.75.75 0 11-1.04-1.08l2.158-1.96H5.75A.75.75 0 015 10z", - clip_rule: "evenodd" - ) - end - end - - def chevron_down_icon - svg( - xmlns: "http://www.w3.org/2000/svg", - viewbox: "0 0 20 20", - fill: "currentColor", - class: "w-4 h-4 ml-1 -mr-1" - ) do |s| - s.path( - fill_rule: "evenodd", - d: - "M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z", - clip_rule: "evenodd" - ) - end - end - - def github_icon - svg( - viewbox: "0 0 16 16", - class: "w-4 h-4", - fill: "currentColor", - aria_hidden: "true" - ) do |s| - s.path( - d: - "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" - ) - end - end - - def account_icon - svg( - xmlns: "http://www.w3.org/2000/svg", - viewbox: "0 0 24 24", - fill: "currentColor", - class: "w-5 h-5" - ) do |s| - s.path( - fill_rule: "evenodd", - d: - "M18.685 19.097A9.723 9.723 0 0021.75 12c0-5.385-4.365-9.75-9.75-9.75S2.25 6.615 2.25 12a9.723 9.723 0 003.065 7.097A9.716 9.716 0 0012 21.75a9.716 9.716 0 006.685-2.653zm-12.54-1.285A7.486 7.486 0 0112 15a7.486 7.486 0 015.855 2.812A8.224 8.224 0 0112 20.25a8.224 8.224 0 01-5.855-2.438zM15.75 9a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z", - clip_rule: "evenodd" - ) - end - end -end diff --git a/app/views/components/shared/sidebar.rb b/app/views/components/shared/sidebar.rb deleted file mode 100644 index ac054d71..00000000 --- a/app/views/components/shared/sidebar.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -class Shared::Sidebar < ApplicationComponent - def view_template - aside(class: "fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block") do - div(class: "relative overflow-hidden h-full py-6 pl-8 pr-6 lg:py-8", style: "position:relative;--radix-scroll-area-corner-width:0px;--radix-scroll-area-corner-height:0px") do - div(class: "h-full w-full rounded-[inherit]", style: "overflow: hidden scroll;", data_controller: "sidebar-menu") do - render Shared::Menu.new - end - end - end - end -end