ruby_ui 1.0.2 → 1.1.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 (68) 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/install/docs_generator.rb +33 -0
  5. data/lib/ruby_ui/accordion/accordion_docs.rb +53 -0
  6. data/lib/ruby_ui/alert/alert_docs.rb +135 -0
  7. data/lib/ruby_ui/alert_dialog/alert_dialog_docs.rb +35 -0
  8. data/lib/ruby_ui/aspect_ratio/aspect_ratio_docs.rb +64 -0
  9. data/lib/ruby_ui/avatar/avatar_docs.rb +92 -0
  10. data/lib/ruby_ui/badge/badge_docs.rb +80 -0
  11. data/lib/ruby_ui/breadcrumb/breadcrumb_docs.rb +116 -0
  12. data/lib/ruby_ui/button/button_docs.rb +143 -0
  13. data/lib/ruby_ui/calendar/calendar_docs.rb +34 -0
  14. data/lib/ruby_ui/card/card_docs.rb +114 -0
  15. data/lib/ruby_ui/carousel/carousel_docs.rb +104 -0
  16. data/lib/ruby_ui/chart/chart_docs.rb +115 -0
  17. data/lib/ruby_ui/checkbox/checkbox.rb +2 -2
  18. data/lib/ruby_ui/checkbox/checkbox_docs.rb +41 -0
  19. data/lib/ruby_ui/clipboard/clipboard_docs.rb +30 -0
  20. data/lib/ruby_ui/codeblock/codeblock_docs.rb +55 -0
  21. data/lib/ruby_ui/collapsible/collapsible_docs.rb +96 -0
  22. data/lib/ruby_ui/combobox/combobox_controller.js +16 -1
  23. data/lib/ruby_ui/combobox/combobox_docs.rb +151 -0
  24. data/lib/ruby_ui/combobox/combobox_popover.rb +2 -1
  25. data/lib/ruby_ui/combobox/combobox_toggle_all_checkbox.rb +3 -2
  26. data/lib/ruby_ui/combobox/combobox_trigger.rb +1 -1
  27. data/lib/ruby_ui/command/command_docs.rb +154 -0
  28. data/lib/ruby_ui/context_menu/context_menu.rb +1 -1
  29. data/lib/ruby_ui/context_menu/context_menu_docs.rb +85 -0
  30. data/lib/ruby_ui/dialog/dialog_docs.rb +102 -0
  31. data/lib/ruby_ui/docs/base.rb +90 -0
  32. data/lib/ruby_ui/docs/component_setup_tabs.rb +15 -0
  33. data/lib/ruby_ui/docs/components_table.rb +13 -0
  34. data/lib/ruby_ui/docs/header.rb +17 -0
  35. data/lib/ruby_ui/docs/sidebar_examples.rb +22 -0
  36. data/lib/ruby_ui/docs/visual_code_example.rb +22 -0
  37. data/lib/ruby_ui/dropdown_menu/dropdown_menu_docs.rb +212 -0
  38. data/lib/ruby_ui/form/form_docs.rb +178 -0
  39. data/lib/ruby_ui/form/form_field.rb +1 -1
  40. data/lib/ruby_ui/form/form_field_error.rb +1 -1
  41. data/lib/ruby_ui/form/form_field_hint.rb +1 -1
  42. data/lib/ruby_ui/form/form_field_label.rb +1 -1
  43. data/lib/ruby_ui/hover_card/hover_card_docs.rb +71 -0
  44. data/lib/ruby_ui/input/input.rb +4 -3
  45. data/lib/ruby_ui/input/input_docs.rb +68 -0
  46. data/lib/ruby_ui/link/link_docs.rb +106 -0
  47. data/lib/ruby_ui/masked_input/masked_input_docs.rb +47 -0
  48. data/lib/ruby_ui/pagination/pagination_docs.rb +127 -0
  49. data/lib/ruby_ui/popover/popover_docs.rb +971 -0
  50. data/lib/ruby_ui/progress/progress_docs.rb +27 -0
  51. data/lib/ruby_ui/radio_button/radio_button.rb +1 -1
  52. data/lib/ruby_ui/radio_button/radio_button_docs.rb +53 -0
  53. data/lib/ruby_ui/select/select_docs.rb +129 -0
  54. data/lib/ruby_ui/separator/separator_docs.rb +36 -0
  55. data/lib/ruby_ui/sheet/sheet_content.rb +1 -1
  56. data/lib/ruby_ui/sheet/sheet_docs.rb +76 -0
  57. data/lib/ruby_ui/shortcut_key/shortcut_key_docs.rb +29 -0
  58. data/lib/ruby_ui/sidebar/sidebar_docs.rb +176 -0
  59. data/lib/ruby_ui/skeleton/skeleton_docs.rb +29 -0
  60. data/lib/ruby_ui/switch/switch_docs.rb +46 -0
  61. data/lib/ruby_ui/table/table_docs.rb +102 -0
  62. data/lib/ruby_ui/tabs/tabs_docs.rb +211 -0
  63. data/lib/ruby_ui/textarea/textarea_docs.rb +54 -0
  64. data/lib/ruby_ui/theme_toggle/theme_toggle_docs.rb +71 -0
  65. data/lib/ruby_ui/tooltip/tooltip_docs.rb +52 -0
  66. data/lib/ruby_ui/typography/typography_docs.rb +107 -0
  67. data/lib/ruby_ui.rb +1 -1
  68. metadata +53 -6
@@ -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
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Views::Docs::Form < Views::Base
4
+ def view_template
5
+ component = "Form"
6
+ div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
7
+ render Docs::Header.new(title: "Form", description: "Building forms with built-in client-side validations.")
8
+
9
+ Heading(level: 2) { "Usage" }
10
+
11
+ render Docs::VisualCodeExample.new(title: "Example", context: self) do
12
+ <<~RUBY
13
+ Form(class: "w-2/3 space-y-6") do
14
+ FormField do
15
+ FormFieldLabel { "Default error" }
16
+ Input(placeholder: "Joel Drapper", required: true, minlength: "3") { "Joel Drapper" }
17
+ FormFieldHint()
18
+ FormFieldError()
19
+ end
20
+ Button(type: "submit") { "Save" }
21
+ end
22
+ RUBY
23
+ end
24
+
25
+ render Docs::VisualCodeExample.new(title: "Disabled", context: self) do
26
+ <<~RUBY
27
+ FormField do
28
+ FormFieldLabel { "Disabled" }
29
+ Input(disabled: true, placeholder: "Joel Drapper", required: true, minlength: "3") { "Joel Drapper" }
30
+ end
31
+ RUBY
32
+ end
33
+
34
+ render Docs::VisualCodeExample.new(title: "Aria Disabled", context: self) do
35
+ <<~RUBY
36
+ FormField do
37
+ FormFieldLabel { "Aria Disabled" }
38
+ Input(aria: {disabled: "true"}, placeholder: "Joel Drapper", required: true, minlength: "3") { "Joel Drapper" }
39
+ end
40
+ RUBY
41
+ end
42
+
43
+ render Docs::VisualCodeExample.new(title: "Custom error message", context: self) do
44
+ <<~RUBY
45
+ Form(class: "w-2/3 space-y-6") do
46
+ FormField do
47
+ FormFieldLabel { "Custom error message" }
48
+ Input(placeholder: "joel@drapper.me", required: true, data_value_missing: "Custom error message")
49
+ FormFieldError()
50
+ end
51
+ Button(type: "submit") { "Save" }
52
+ end
53
+ RUBY
54
+ end
55
+
56
+ render Docs::VisualCodeExample.new(title: "Backend error", context: self) do
57
+ <<~RUBY
58
+ Form(class: "w-2/3 space-y-6") do
59
+ FormField do
60
+ FormFieldLabel { "Backend error" }
61
+ Input(placeholder: "Joel Drapper", required: true)
62
+ FormFieldError { "Error from backend" }
63
+ end
64
+ Button(type: "submit") { "Save" }
65
+ end
66
+ RUBY
67
+ end
68
+
69
+ render Docs::VisualCodeExample.new(title: "Checkbox", context: self) do
70
+ <<~RUBY
71
+ Form(class: "w-2/3 space-y-6") do
72
+ FormField do
73
+ Checkbox(required: true)
74
+ label(
75
+ class:
76
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
77
+ ) { " Accept terms and conditions " }
78
+ FormFieldError()
79
+ end
80
+ Button(type: "submit") { "Save" }
81
+ end
82
+ RUBY
83
+ end
84
+
85
+ render Docs::VisualCodeExample.new(title: "Select", context: self) do
86
+ <<~RUBY
87
+ Form(class: "w-2/3 space-y-6") do
88
+ FormField do
89
+ FormFieldLabel { "Select" }
90
+ Select do
91
+ SelectInput(required: true)
92
+ SelectTrigger do
93
+ SelectValue(placeholder: "Select a fruit")
94
+ end
95
+ SelectContent() do
96
+ SelectGroup do
97
+ SelectLabel { "Fruits" }
98
+ SelectItem(value: "apple") { "Apple" }
99
+ SelectItem(value: "orange") { "Orange" }
100
+ SelectItem(value: "banana") { "Banana" }
101
+ SelectItem(value: "watermelon") { "Watermelon" }
102
+ end
103
+ end
104
+ end
105
+ FormFieldError()
106
+ end
107
+ Button(type: "submit") { "Save" }
108
+ end
109
+ RUBY
110
+ end
111
+
112
+ render Docs::VisualCodeExample.new(title: "Combobox", context: self) do
113
+ <<~RUBY
114
+ Form(class: "w-2/3 space-y-6") do
115
+ FormField do
116
+ FormFieldLabel { "Combobox" }
117
+
118
+ Combobox do
119
+ ComboboxTrigger placeholder: "Pick value"
120
+
121
+ ComboboxPopover do
122
+ ComboboxSearchInput(placeholder: "Pick value or type anything")
123
+
124
+ ComboboxList do
125
+ ComboboxEmptyState { "No result" }
126
+
127
+ ComboboxListGroup label: "Fruits" do
128
+ ComboboxItem do
129
+ ComboboxRadio(name: "food", value: "apple", required: true)
130
+ span { "Apple" }
131
+ end
132
+
133
+ ComboboxItem do
134
+ ComboboxRadio(name: "food", value: "banana", required: true)
135
+ span { "Banana" }
136
+ end
137
+ end
138
+
139
+ ComboboxListGroup label: "Vegetable" do
140
+ ComboboxItem do
141
+ ComboboxRadio(name: "food", value: "brocoli", required: true)
142
+ span { "Broccoli" }
143
+ end
144
+
145
+ ComboboxItem do
146
+ ComboboxRadio(name: "food", value: "carrot", required: true)
147
+ span { "Carrot" }
148
+ end
149
+ end
150
+
151
+ ComboboxListGroup label: "Others" do
152
+ ComboboxItem do
153
+ ComboboxRadio(name: "food", value: "chocolate", required: true)
154
+ span { "Chocolate" }
155
+ end
156
+
157
+ ComboboxItem do
158
+ ComboboxRadio(name: "food", value: "milk", required: true)
159
+ span { "Milk" }
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ FormFieldError()
167
+ end
168
+ Button(type: "submit") { "Save" }
169
+ end
170
+ RUBY
171
+ end
172
+
173
+ render Components::ComponentSetup::Tabs.new(component_name: component)
174
+
175
+ render Docs::ComponentsTable.new(component_files(component))
176
+ end
177
+ end
178
+ end
@@ -13,7 +13,7 @@ module RubyUI
13
13
  data: {
14
14
  controller: "ruby-ui--form-field"
15
15
  },
16
- class: "space-y-2"
16
+ class: "flex flex-col gap-2"
17
17
  }
18
18
  end
19
19
  end
@@ -13,7 +13,7 @@ module RubyUI
13
13
  data: {
14
14
  ruby_ui__form_field_target: "error"
15
15
  },
16
- class: "text-sm font-medium text-destructive"
16
+ class: "empty:hidden text-sm font-medium text-destructive"
17
17
  }
18
18
  end
19
19
  end
@@ -9,7 +9,7 @@ module RubyUI
9
9
  private
10
10
 
11
11
  def default_attrs
12
- {class: "text-sm text-muted-foreground"}
12
+ {class: "empty:hidden text-sm text-muted-foreground"}
13
13
  end
14
14
  end
15
15
  end
@@ -11,7 +11,7 @@ module RubyUI
11
11
  def default_attrs
12
12
  {
13
13
  class: [
14
- "text-sm font-medium leading-none",
14
+ "empty:hidden text-sm font-medium leading-none",
15
15
  "peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
16
16
  "peer-aria-disabled:cursor-not-allowed peer-aria-disabled:opacity-70 peer-aria-disabled:pointer-events-none"
17
17
  ]