ruby_ui 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -0
  4. data/lib/generators/ruby_ui/component_generator.rb +5 -1
  5. data/lib/generators/ruby_ui/dependencies.yml +10 -0
  6. data/lib/generators/ruby_ui/install/docs_generator.rb +33 -0
  7. data/lib/generators/ruby_ui/install/install_generator.rb +1 -1
  8. data/lib/generators/ruby_ui/javascript_utils.rb +4 -0
  9. data/lib/ruby_ui/accordion/accordion_docs.rb +53 -0
  10. data/lib/ruby_ui/alert/alert_docs.rb +135 -0
  11. data/lib/ruby_ui/alert_dialog/alert_dialog_docs.rb +35 -0
  12. data/lib/ruby_ui/aspect_ratio/aspect_ratio_docs.rb +64 -0
  13. data/lib/ruby_ui/avatar/avatar_docs.rb +92 -0
  14. data/lib/ruby_ui/badge/badge_docs.rb +80 -0
  15. data/lib/ruby_ui/breadcrumb/breadcrumb_docs.rb +116 -0
  16. data/lib/ruby_ui/button/button_docs.rb +143 -0
  17. data/lib/ruby_ui/calendar/calendar_docs.rb +34 -0
  18. data/lib/ruby_ui/card/card_docs.rb +114 -0
  19. data/lib/ruby_ui/carousel/carousel_docs.rb +104 -0
  20. data/lib/ruby_ui/chart/chart_docs.rb +115 -0
  21. data/lib/ruby_ui/checkbox/checkbox.rb +2 -2
  22. data/lib/ruby_ui/checkbox/checkbox_docs.rb +41 -0
  23. data/lib/ruby_ui/clipboard/clipboard_docs.rb +30 -0
  24. data/lib/ruby_ui/codeblock/codeblock_docs.rb +55 -0
  25. data/lib/ruby_ui/collapsible/collapsible_docs.rb +96 -0
  26. data/lib/ruby_ui/combobox/combobox.rb +7 -1
  27. data/lib/ruby_ui/combobox/combobox_badge.rb +17 -0
  28. data/lib/ruby_ui/combobox/combobox_badge_trigger.rb +47 -0
  29. data/lib/ruby_ui/combobox/combobox_checkbox.rb +1 -7
  30. data/lib/ruby_ui/combobox/combobox_clear_button.rb +40 -0
  31. data/lib/ruby_ui/combobox/combobox_controller.js +252 -47
  32. data/lib/ruby_ui/combobox/combobox_docs.rb +286 -0
  33. data/lib/ruby_ui/combobox/combobox_input_trigger.rb +64 -0
  34. data/lib/ruby_ui/combobox/combobox_item.rb +5 -7
  35. data/lib/ruby_ui/combobox/combobox_item_indicator.rb +30 -0
  36. data/lib/ruby_ui/combobox/combobox_list_group.rb +1 -1
  37. data/lib/ruby_ui/combobox/combobox_popover.rb +1 -5
  38. data/lib/ruby_ui/combobox/combobox_radio.rb +1 -8
  39. data/lib/ruby_ui/combobox/combobox_toggle_all_checkbox.rb +1 -6
  40. data/lib/ruby_ui/combobox/combobox_trigger.rb +19 -19
  41. data/lib/ruby_ui/command/command_docs.rb +154 -0
  42. data/lib/ruby_ui/context_menu/context_menu.rb +1 -1
  43. data/lib/ruby_ui/context_menu/context_menu_docs.rb +85 -0
  44. data/lib/ruby_ui/data_table/data_table.rb +29 -0
  45. data/lib/ruby_ui/data_table/data_table_bulk_actions.rb +18 -0
  46. data/lib/ruby_ui/data_table/data_table_column_toggle.rb +62 -0
  47. data/lib/ruby_ui/data_table/data_table_column_visibility_controller.js +14 -0
  48. data/lib/ruby_ui/data_table/data_table_controller.js +57 -0
  49. data/lib/ruby_ui/data_table/data_table_docs.rb +180 -0
  50. data/lib/ruby_ui/data_table/data_table_expand_toggle.rb +53 -0
  51. data/lib/ruby_ui/data_table/data_table_form.rb +39 -0
  52. data/lib/ruby_ui/data_table/data_table_kaminari_adapter.rb +17 -0
  53. data/lib/ruby_ui/data_table/data_table_manual_adapter.rb +17 -0
  54. data/lib/ruby_ui/data_table/data_table_pagination.rb +100 -0
  55. data/lib/ruby_ui/data_table/data_table_pagination_bar.rb +15 -0
  56. data/lib/ruby_ui/data_table/data_table_pagy_adapter.rb +17 -0
  57. data/lib/ruby_ui/data_table/data_table_per_page_select.rb +35 -0
  58. data/lib/ruby_ui/data_table/data_table_row_checkbox.rb +30 -0
  59. data/lib/ruby_ui/data_table/data_table_search.rb +57 -0
  60. data/lib/ruby_ui/data_table/data_table_search_controller.js +62 -0
  61. data/lib/ruby_ui/data_table/data_table_select_all_checkbox.rb +21 -0
  62. data/lib/ruby_ui/data_table/data_table_selection_summary.rb +25 -0
  63. data/lib/ruby_ui/data_table/data_table_sort_head.rb +112 -0
  64. data/lib/ruby_ui/data_table/data_table_toolbar.rb +15 -0
  65. data/lib/ruby_ui/dialog/dialog_docs.rb +102 -0
  66. data/lib/ruby_ui/docs/base.rb +90 -0
  67. data/lib/ruby_ui/docs/component_setup_tabs.rb +15 -0
  68. data/lib/ruby_ui/docs/components_table.rb +13 -0
  69. data/lib/ruby_ui/docs/header.rb +17 -0
  70. data/lib/ruby_ui/docs/sidebar_examples.rb +22 -0
  71. data/lib/ruby_ui/docs/visual_code_example.rb +22 -0
  72. data/lib/ruby_ui/dropdown_menu/dropdown_menu_docs.rb +212 -0
  73. data/lib/ruby_ui/form/form_docs.rb +178 -0
  74. data/lib/ruby_ui/form/form_field.rb +1 -1
  75. data/lib/ruby_ui/form/form_field_error.rb +1 -1
  76. data/lib/ruby_ui/form/form_field_hint.rb +1 -1
  77. data/lib/ruby_ui/form/form_field_label.rb +1 -1
  78. data/lib/ruby_ui/hover_card/hover_card_docs.rb +71 -0
  79. data/lib/ruby_ui/input/input.rb +4 -3
  80. data/lib/ruby_ui/input/input_docs.rb +68 -0
  81. data/lib/ruby_ui/link/link_docs.rb +106 -0
  82. data/lib/ruby_ui/masked_input/masked_input.rb +11 -1
  83. data/lib/ruby_ui/masked_input/masked_input_controller.js +13 -0
  84. data/lib/ruby_ui/masked_input/masked_input_docs.rb +47 -0
  85. data/lib/ruby_ui/native_select/native_select.rb +39 -0
  86. data/lib/ruby_ui/native_select/native_select_docs.rb +83 -0
  87. data/lib/ruby_ui/native_select/native_select_group.rb +15 -0
  88. data/lib/ruby_ui/native_select/native_select_icon.rb +39 -0
  89. data/lib/ruby_ui/native_select/native_select_option.rb +15 -0
  90. data/lib/ruby_ui/pagination/pagination_docs.rb +127 -0
  91. data/lib/ruby_ui/popover/popover_docs.rb +971 -0
  92. data/lib/ruby_ui/progress/progress_docs.rb +27 -0
  93. data/lib/ruby_ui/radio_button/radio_button.rb +1 -1
  94. data/lib/ruby_ui/radio_button/radio_button_docs.rb +53 -0
  95. data/lib/ruby_ui/select/select_docs.rb +129 -0
  96. data/lib/ruby_ui/separator/separator_docs.rb +36 -0
  97. data/lib/ruby_ui/sheet/sheet_content.rb +1 -1
  98. data/lib/ruby_ui/sheet/sheet_docs.rb +76 -0
  99. data/lib/ruby_ui/shortcut_key/shortcut_key_docs.rb +29 -0
  100. data/lib/ruby_ui/sidebar/sidebar_docs.rb +176 -0
  101. data/lib/ruby_ui/skeleton/skeleton_docs.rb +29 -0
  102. data/lib/ruby_ui/switch/switch_docs.rb +46 -0
  103. data/lib/ruby_ui/table/table_docs.rb +102 -0
  104. data/lib/ruby_ui/tabs/tabs_docs.rb +211 -0
  105. data/lib/ruby_ui/textarea/textarea_docs.rb +54 -0
  106. data/lib/ruby_ui/theme_toggle/theme_toggle_docs.rb +71 -0
  107. data/lib/ruby_ui/tooltip/tooltip_docs.rb +52 -0
  108. data/lib/ruby_ui/typography/typography_docs.rb +107 -0
  109. data/lib/ruby_ui.rb +1 -1
  110. metadata +90 -3
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+
5
+ module RubyUI
6
+ class DataTableSortHead < Base
7
+ def initialize(column_key:, label:, sort: nil, direction: nil, sort_param: "sort", direction_param: "direction", page_param: "page", path: "", query: {}, **attrs)
8
+ @column_key = column_key
9
+ @label = label
10
+ @sort = sort
11
+ @direction = direction
12
+ @sort_param = sort_param
13
+ @direction_param = direction_param
14
+ @page_param = page_param
15
+ @path = path
16
+ @query = query.to_h.transform_keys(&:to_s)
17
+ super(**attrs)
18
+ end
19
+
20
+ def view_template
21
+ render RubyUI::TableHead.new(class: "text-foreground whitespace-nowrap", **attrs) do
22
+ a(href: sort_href, class: "inline-flex items-center gap-1 text-inherit no-underline hover:text-foreground transition-colors") do
23
+ plain @label
24
+ sort_icon
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def current_direction
32
+ (@sort.to_s == @column_key.to_s) ? @direction : nil
33
+ end
34
+
35
+ def next_params
36
+ next_dir = {nil => "asc", "asc" => "desc", "desc" => nil}[current_direction]
37
+ base = @query.except(@sort_param, @direction_param, @page_param)
38
+ next_dir ? base.merge(@sort_param => @column_key.to_s, @direction_param => next_dir) : base
39
+ end
40
+
41
+ def sort_href
42
+ qs = build_query(next_params)
43
+ qs.empty? ? @path : "#{@path}?#{qs}"
44
+ end
45
+
46
+ def build_query(hash)
47
+ hash.flat_map { |k, v|
48
+ Array(v).map { |val| "#{CGI.escape(k.to_s)}=#{CGI.escape(val.to_s)}" }
49
+ }.join("&")
50
+ end
51
+
52
+ def sort_icon
53
+ icon_name = case current_direction
54
+ when "asc" then :chevron_up
55
+ when "desc" then :chevron_down
56
+ else :chevrons_up_down
57
+ end
58
+ icon_class = current_direction ? "inline-block w-3 h-3" : "inline-block w-3 h-3 opacity-30"
59
+ render_sort_svg(icon_name, icon_class)
60
+ end
61
+
62
+ def render_sort_svg(icon_name, icon_class)
63
+ case icon_name
64
+ when :chevron_up
65
+ # chevron-up: polyline pointing up
66
+ svg(
67
+ xmlns: "http://www.w3.org/2000/svg",
68
+ width: "12",
69
+ height: "12",
70
+ viewBox: "0 0 24 24",
71
+ fill: "none",
72
+ stroke: "currentColor",
73
+ stroke_width: "2",
74
+ stroke_linecap: "round",
75
+ stroke_linejoin: "round",
76
+ class: icon_class
77
+ ) { |s| s.polyline(points: "18 15 12 9 6 15") }
78
+ when :chevron_down
79
+ # chevron-down: polyline pointing down
80
+ svg(
81
+ xmlns: "http://www.w3.org/2000/svg",
82
+ width: "12",
83
+ height: "12",
84
+ viewBox: "0 0 24 24",
85
+ fill: "none",
86
+ stroke: "currentColor",
87
+ stroke_width: "2",
88
+ stroke_linecap: "round",
89
+ stroke_linejoin: "round",
90
+ class: icon_class
91
+ ) { |s| s.polyline(points: "6 9 12 15 18 9") }
92
+ else
93
+ # chevrons-up-down
94
+ svg(
95
+ xmlns: "http://www.w3.org/2000/svg",
96
+ width: "12",
97
+ height: "12",
98
+ viewBox: "0 0 24 24",
99
+ fill: "none",
100
+ stroke: "currentColor",
101
+ stroke_width: "2",
102
+ stroke_linecap: "round",
103
+ stroke_linejoin: "round",
104
+ class: icon_class
105
+ ) do |s|
106
+ s.polyline(points: "8 15 12 19 16 15")
107
+ s.polyline(points: "8 9 12 5 16 9")
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class DataTableToolbar < Base
5
+ def view_template(&)
6
+ div(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {class: "flex items-center justify-between gap-2"}
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Views::Docs::Dialog < Views::Base
4
+ def view_template
5
+ component = "Dialog"
6
+
7
+ div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
8
+ render Docs::Header.new(title: "Dialog", description: "A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.")
9
+
10
+ Heading(level: 2) { "Usage" }
11
+
12
+ render Docs::VisualCodeExample.new(title: "Example", context: self) do
13
+ <<~RUBY
14
+ Dialog do
15
+ DialogTrigger do
16
+ Button { "Open Dialog" }
17
+ end
18
+ DialogContent do
19
+ DialogHeader do
20
+ DialogTitle { "RubyUI to the rescue" }
21
+ DialogDescription { "RubyUI helps you build accessible standard compliant web apps with ease" }
22
+ end
23
+ DialogMiddle do
24
+ AspectRatio(aspect_ratio: "16/9", class: 'rounded-md overflow-hidden border') do
25
+ img(
26
+ alt: "Placeholder",
27
+ loading: "lazy",
28
+ src: image_path("pattern.jpg")
29
+ )
30
+ end
31
+ end
32
+ DialogFooter do
33
+ Button(variant: :outline, data: { action: 'click->ruby-ui--dialog#dismiss' }) { "Cancel" }
34
+ Button { "Save" }
35
+ end
36
+ end
37
+ end
38
+ RUBY
39
+ end
40
+
41
+ render Docs::VisualCodeExample.new(title: "Size", description: "Applicable for wider screens", context: self) do
42
+ <<~RUBY
43
+ div(class: 'flex flex-wrap justify-center gap-2') do
44
+ Dialog do
45
+ DialogTrigger do
46
+ Button { "Small Dialog" }
47
+ end
48
+ DialogContent(size: :sm) do
49
+ DialogHeader do
50
+ DialogTitle { "RubyUI to the rescue" }
51
+ DialogDescription { "RubyUI helps you build accessible standard compliant web apps with ease" }
52
+ end
53
+ DialogMiddle do
54
+ AspectRatio(aspect_ratio: "16/9", class: 'rounded-md overflow-hidden border') do
55
+ img(
56
+ alt: "Placeholder",
57
+ loading: "lazy",
58
+ src: image_path("pattern.jpg")
59
+ )
60
+ end
61
+ end
62
+ DialogFooter do
63
+ Button(variant: :outline, data: { action: 'click->ruby-ui--dialog#dismiss' }) { "Cancel" }
64
+ Button { "Save" }
65
+ end
66
+ end
67
+ end
68
+
69
+ Dialog do
70
+ DialogTrigger do
71
+ Button { "Large Dialog" }
72
+ end
73
+ DialogContent(size: :lg) do
74
+ DialogHeader do
75
+ DialogTitle { "RubyUI to the rescue" }
76
+ DialogDescription { "RubyUI helps you build accessible standard compliant web apps with ease" }
77
+ end
78
+ DialogMiddle do
79
+ AspectRatio(aspect_ratio: "16/9", class: 'rounded-md overflow-hidden border') do
80
+ img(
81
+ alt: "Placeholder",
82
+ loading: "lazy",
83
+ src: image_path("pattern.jpg")
84
+ )
85
+ end
86
+ end
87
+ DialogFooter do
88
+ Button(variant: :outline, data: { action: 'click->ruby-ui--dialog#dismiss' }) { "Cancel" }
89
+ Button { "Save" }
90
+ end
91
+ end
92
+ end
93
+ end
94
+ RUBY
95
+ end
96
+
97
+ render Components::ComponentSetup::Tabs.new(component_name: component)
98
+
99
+ render Docs::ComponentsTable.new(component_files(component))
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Views
4
+ class Base < Phlex::HTML
5
+ def Heading(level:, &)
6
+ tag = :"h#{level}"
7
+ send(tag, &)
8
+ end
9
+
10
+ def component_files(component_name)
11
+ []
12
+ end
13
+
14
+ # Text helper for wrapping paragraphs
15
+ def Text(&)
16
+ p(&)
17
+ end
18
+
19
+ # InlineLink helper for documentation links
20
+ def InlineLink(href:, target: nil, class: nil, &)
21
+ a(href: href, target: target, class: binding.local_variable_get(:class), &)
22
+ end
23
+
24
+ # Alert component helpers
25
+ def Alert(&)
26
+ div(&)
27
+ end
28
+
29
+ def AlertTitle(&)
30
+ h4(&)
31
+ end
32
+
33
+ def AlertDescription(&)
34
+ p(&)
35
+ end
36
+
37
+ # Route helper stubs - return "#" as placeholder
38
+ def docs_sheet_path
39
+ "#"
40
+ end
41
+
42
+ def docs_separator_path
43
+ "#"
44
+ end
45
+
46
+ def docs_accordion_path
47
+ "#"
48
+ end
49
+
50
+ def docs_alert_path
51
+ "#"
52
+ end
53
+
54
+ def docs_alert_dialog_path
55
+ "#"
56
+ end
57
+
58
+ def docs_aspect_ratio_path
59
+ "#"
60
+ end
61
+
62
+ def docs_avatar_path
63
+ "#"
64
+ end
65
+
66
+ def docs_badge_path
67
+ "#"
68
+ end
69
+
70
+ def docs_installation_path
71
+ "#"
72
+ end
73
+
74
+ # InlineCode helper for typography examples
75
+ def InlineCode(&)
76
+ code(&)
77
+ end
78
+ end
79
+ end
80
+
81
+ # Module-level components stub
82
+ module Components
83
+ def self.Heading(level:, &block)
84
+ # Stub for module-level Heading calls
85
+ end
86
+
87
+ def self.TypographyList(items:, numbered: false)
88
+ # Stub for TypographyList component
89
+ end
90
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Components
4
+ module ComponentSetup
5
+ class Tabs < Phlex::HTML
6
+ def initialize(component_name:)
7
+ @component_name = component_name
8
+ end
9
+
10
+ def view_template
11
+ # Minimal stub - empty by default
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docs
4
+ class ComponentsTable < Phlex::HTML
5
+ def initialize(files)
6
+ @files = files
7
+ end
8
+
9
+ def view_template
10
+ # Minimal stub - empty by default
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docs
4
+ class Header < Phlex::HTML
5
+ def initialize(title:, description: nil)
6
+ @title = title
7
+ @description = description
8
+ end
9
+
10
+ def view_template
11
+ div do
12
+ h1 { @title }
13
+ p { @description } if @description
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Stub constants for sidebar documentation examples
4
+ # These are replaced with actual implementations in the web app
5
+
6
+ module Views
7
+ module Docs
8
+ class Sidebar < Views::Base
9
+ class Example
10
+ CODE = <<~RUBY
11
+ # Sidebar example code placeholder
12
+ RUBY
13
+ end
14
+
15
+ class InsetExample
16
+ CODE = <<~RUBY
17
+ # Sidebar inset example code placeholder
18
+ RUBY
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docs
4
+ class VisualCodeExample < Phlex::HTML
5
+ def initialize(title:, context:, description: nil, src: nil)
6
+ @title = title
7
+ @context = context
8
+ @description = description
9
+ @src = src
10
+ end
11
+
12
+ def view_template(&block)
13
+ code = block.call
14
+ div do
15
+ h3 { @title }
16
+ p { @description } if @description
17
+ pre { code }
18
+ @context.instance_eval(code)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Views::Docs::DropdownMenu < Views::Base
4
+ def view_template
5
+ component = "DropdownMenu"
6
+ div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
7
+ render Docs::Header.new(title: "Dropdown Menu", description: "Displays a menu to the user — such as a set of actions or functions — triggered by a button.")
8
+
9
+ Heading(level: 2) { "Usage" }
10
+
11
+ render Docs::VisualCodeExample.new(title: "Example", context: self) do
12
+ <<~RUBY
13
+ DropdownMenu do
14
+ DropdownMenuTrigger(class: 'w-full') do
15
+ Button(variant: :outline) { "Open" }
16
+ end
17
+ DropdownMenuContent do
18
+ DropdownMenuLabel { "My Account" }
19
+ DropdownMenuSeparator
20
+ DropdownMenuItem(href: '#') { "Profile" }
21
+ DropdownMenuItem(href: '#') { "Billing" }
22
+ DropdownMenuItem(href: '#') { "Team" }
23
+ DropdownMenuItem(href: '#') { "Subscription" }
24
+ end
25
+ end
26
+ RUBY
27
+ end
28
+
29
+ render Docs::VisualCodeExample.new(title: "Placement", description: "If the DropdownMenu conflicts with edge, it will auto-adjust it's placement", context: self) do
30
+ <<~RUBY
31
+ div(class: 'grid grid-cols-1 sm:grid-cols-3 gap-4') do
32
+ # -- TOP --
33
+ DropdownMenu(options: { placement: 'top' }) do
34
+ DropdownMenuTrigger(class: 'w-full') do
35
+ Button(variant: :outline, class: 'w-full justify-center') { 'top' }
36
+ end
37
+ DropdownMenuContent do
38
+ DropdownMenuLabel { "My Account" }
39
+ DropdownMenuSeparator
40
+ DropdownMenuItem(href: '#') { "Profile" }
41
+ DropdownMenuItem(href: '#') { "Billing" }
42
+ DropdownMenuItem(href: '#') { "Team" }
43
+ DropdownMenuItem(href: '#') { "Subscription" }
44
+ end
45
+ end
46
+
47
+ DropdownMenu(options: { placement: 'top-start' }) do
48
+ DropdownMenuTrigger(class: 'w-full') do
49
+ Button(variant: :outline, class: 'w-full justify-center') { 'top-start' }
50
+ end
51
+ DropdownMenuContent do
52
+ DropdownMenuLabel { "My Account" }
53
+ DropdownMenuSeparator
54
+ DropdownMenuItem(href: '#') { "Profile" }
55
+ DropdownMenuItem(href: '#') { "Billing" }
56
+ DropdownMenuItem(href: '#') { "Team" }
57
+ DropdownMenuItem(href: '#') { "Subscription" }
58
+ end
59
+ end
60
+
61
+ DropdownMenu(options: { placement: 'top-end' }) do
62
+ DropdownMenuTrigger(class: 'w-full') do
63
+ Button(variant: :outline, class: 'w-full justify-center') { 'top-end' }
64
+ end
65
+ DropdownMenuContent do
66
+ DropdownMenuLabel { "My Account" }
67
+ DropdownMenuSeparator
68
+ DropdownMenuItem(href: '#') { "Profile" }
69
+ DropdownMenuItem(href: '#') { "Billing" }
70
+ DropdownMenuItem(href: '#') { "Team" }
71
+ DropdownMenuItem(href: '#') { "Subscription" }
72
+ end
73
+ end
74
+
75
+ # -- BOTTOM --
76
+ DropdownMenu(options: { placement: 'bottom' }) do
77
+ DropdownMenuTrigger(class: 'w-full') do
78
+ Button(variant: :outline, class: 'w-full justify-center') { 'bottom' }
79
+ end
80
+ DropdownMenuContent do
81
+ DropdownMenuLabel { "My Account" }
82
+ DropdownMenuSeparator
83
+ DropdownMenuItem(href: '#') { "Profile" }
84
+ DropdownMenuItem(href: '#') { "Billing" }
85
+ DropdownMenuItem(href: '#') { "Team" }
86
+ DropdownMenuItem(href: '#') { "Subscription" }
87
+ end
88
+ end
89
+
90
+ DropdownMenu(options: { placement: 'bottom-start' }) do
91
+ DropdownMenuTrigger(class: 'w-full') do
92
+ Button(variant: :outline, class: 'w-full justify-center') { 'bottom-start' }
93
+ end
94
+ DropdownMenuContent do
95
+ DropdownMenuLabel { "My Account" }
96
+ DropdownMenuSeparator
97
+ DropdownMenuItem(href: '#') { "Profile" }
98
+ DropdownMenuItem(href: '#') { "Billing" }
99
+ DropdownMenuItem(href: '#') { "Team" }
100
+ DropdownMenuItem(href: '#') { "Subscription" }
101
+ end
102
+ end
103
+
104
+ DropdownMenu(options: { placement: 'bottom-end' }) do
105
+ DropdownMenuTrigger(class: 'w-full') do
106
+ Button(variant: :outline, class: 'w-full justify-center') { 'bottom-end' }
107
+ end
108
+ DropdownMenuContent do
109
+ DropdownMenuLabel { "My Account" }
110
+ DropdownMenuSeparator
111
+ DropdownMenuItem(href: '#') { "Profile" }
112
+ DropdownMenuItem(href: '#') { "Billing" }
113
+ DropdownMenuItem(href: '#') { "Team" }
114
+ DropdownMenuItem(href: '#') { "Subscription" }
115
+ end
116
+ end
117
+
118
+ # -- LEFT --
119
+ DropdownMenu(options: { placement: 'left' }) do
120
+ DropdownMenuTrigger(class: 'w-full') do
121
+ Button(variant: :outline, class: 'w-full justify-center') { 'left' }
122
+ end
123
+ DropdownMenuContent do
124
+ DropdownMenuLabel { "My Account" }
125
+ DropdownMenuSeparator
126
+ DropdownMenuItem(href: '#') { "Profile" }
127
+ DropdownMenuItem(href: '#') { "Billing" }
128
+ DropdownMenuItem(href: '#') { "Team" }
129
+ DropdownMenuItem(href: '#') { "Subscription" }
130
+ end
131
+ end
132
+
133
+ DropdownMenu(options: { placement: 'left-start' }) do
134
+ DropdownMenuTrigger(class: 'w-full') do
135
+ Button(variant: :outline, class: 'w-full justify-center') { 'left-start' }
136
+ end
137
+ DropdownMenuContent do
138
+ DropdownMenuLabel { "My Account" }
139
+ DropdownMenuSeparator
140
+ DropdownMenuItem(href: '#') { "Profile" }
141
+ DropdownMenuItem(href: '#') { "Billing" }
142
+ DropdownMenuItem(href: '#') { "Team" }
143
+ DropdownMenuItem(href: '#') { "Subscription" }
144
+ end
145
+ end
146
+
147
+ DropdownMenu(options: { placement: 'left-end' }) do
148
+ DropdownMenuTrigger(class: 'w-full') do
149
+ Button(variant: :outline, class: 'w-full justify-center') { 'left-end' }
150
+ end
151
+ DropdownMenuContent do
152
+ DropdownMenuLabel { "My Account" }
153
+ DropdownMenuSeparator
154
+ DropdownMenuItem(href: '#') { "Profile" }
155
+ DropdownMenuItem(href: '#') { "Billing" }
156
+ DropdownMenuItem(href: '#') { "Team" }
157
+ DropdownMenuItem(href: '#') { "Subscription" }
158
+ end
159
+ end
160
+
161
+ # -- RIGHT --
162
+ DropdownMenu(options: { placement: 'right' }) do
163
+ DropdownMenuTrigger(class: 'w-full') do
164
+ Button(variant: :outline, class: 'w-full justify-center') { 'right' }
165
+ end
166
+ DropdownMenuContent do
167
+ DropdownMenuLabel { "My Account" }
168
+ DropdownMenuSeparator
169
+ DropdownMenuItem(href: '#') { "Profile" }
170
+ DropdownMenuItem(href: '#') { "Billing" }
171
+ DropdownMenuItem(href: '#') { "Team" }
172
+ DropdownMenuItem(href: '#') { "Subscription" }
173
+ end
174
+ end
175
+
176
+ DropdownMenu(options: { placement: 'right-start' }) do
177
+ DropdownMenuTrigger(class: 'w-full') do
178
+ Button(variant: :outline, class: 'w-full justify-center') { 'right-start' }
179
+ end
180
+ DropdownMenuContent do
181
+ DropdownMenuLabel { "My Account" }
182
+ DropdownMenuSeparator
183
+ DropdownMenuItem(href: '#') { "Profile" }
184
+ DropdownMenuItem(href: '#') { "Billing" }
185
+ DropdownMenuItem(href: '#') { "Team" }
186
+ DropdownMenuItem(href: '#') { "Subscription" }
187
+ end
188
+ end
189
+
190
+ DropdownMenu(options: { placement: 'right-end' }) do
191
+ DropdownMenuTrigger(class: 'w-full') do
192
+ Button(variant: :outline, class: 'w-full justify-center') { 'right-end' }
193
+ end
194
+ DropdownMenuContent do
195
+ DropdownMenuLabel { "My Account" }
196
+ DropdownMenuSeparator
197
+ DropdownMenuItem(href: '#') { "Profile" }
198
+ DropdownMenuItem(href: '#') { "Billing" }
199
+ DropdownMenuItem(href: '#') { "Team" }
200
+ DropdownMenuItem(href: '#') { "Subscription" }
201
+ end
202
+ end
203
+ end
204
+ RUBY
205
+ end
206
+
207
+ render Components::ComponentSetup::Tabs.new(component_name: component)
208
+
209
+ render Docs::ComponentsTable.new(component_files(component))
210
+ end
211
+ end
212
+ end