ruby_ui 1.0.1 → 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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +6 -0
  4. data/lib/generators/ruby_ui/component/all_generator.rb +22 -0
  5. data/lib/generators/ruby_ui/component_generator.rb +4 -3
  6. data/lib/generators/ruby_ui/install/docs_generator.rb +33 -0
  7. data/lib/generators/ruby_ui/install/install_generator.rb +1 -7
  8. data/lib/generators/ruby_ui/install/templates/tailwind.css.erb +0 -3
  9. data/lib/generators/ruby_ui/javascript_utils.rb +4 -0
  10. data/lib/ruby_ui/accordion/accordion_docs.rb +53 -0
  11. data/lib/ruby_ui/alert/alert_docs.rb +135 -0
  12. data/lib/ruby_ui/alert_dialog/alert_dialog_content.rb +2 -3
  13. data/lib/ruby_ui/alert_dialog/alert_dialog_docs.rb +35 -0
  14. data/lib/ruby_ui/aspect_ratio/aspect_ratio_docs.rb +64 -0
  15. data/lib/ruby_ui/avatar/avatar_docs.rb +92 -0
  16. data/lib/ruby_ui/badge/badge_docs.rb +80 -0
  17. data/lib/ruby_ui/breadcrumb/breadcrumb_docs.rb +116 -0
  18. data/lib/ruby_ui/button/button.rb +32 -16
  19. data/lib/ruby_ui/button/button_docs.rb +143 -0
  20. data/lib/ruby_ui/calendar/calendar_docs.rb +34 -0
  21. data/lib/ruby_ui/card/card_docs.rb +114 -0
  22. data/lib/ruby_ui/carousel/carousel_docs.rb +104 -0
  23. data/lib/ruby_ui/chart/chart_docs.rb +115 -0
  24. data/lib/ruby_ui/checkbox/checkbox.rb +7 -1
  25. data/lib/ruby_ui/checkbox/checkbox_docs.rb +41 -0
  26. data/lib/ruby_ui/clipboard/clipboard_docs.rb +30 -0
  27. data/lib/ruby_ui/codeblock/codeblock_docs.rb +55 -0
  28. data/lib/ruby_ui/collapsible/collapsible_docs.rb +96 -0
  29. data/lib/ruby_ui/combobox/combobox.rb +3 -2
  30. data/lib/ruby_ui/combobox/combobox_checkbox.rb +4 -2
  31. data/lib/ruby_ui/combobox/combobox_controller.js +20 -5
  32. data/lib/ruby_ui/combobox/combobox_docs.rb +151 -0
  33. data/lib/ruby_ui/combobox/combobox_item.rb +2 -1
  34. data/lib/ruby_ui/combobox/combobox_popover.rb +2 -1
  35. data/lib/ruby_ui/combobox/combobox_radio.rb +8 -1
  36. data/lib/ruby_ui/combobox/combobox_search_input.rb +11 -5
  37. data/lib/ruby_ui/combobox/combobox_toggle_all_checkbox.rb +4 -2
  38. data/lib/ruby_ui/combobox/combobox_trigger.rb +8 -2
  39. data/lib/ruby_ui/command/command_controller.js +0 -1
  40. data/lib/ruby_ui/command/command_docs.rb +154 -0
  41. data/lib/ruby_ui/context_menu/context_menu.rb +1 -1
  42. data/lib/ruby_ui/context_menu/context_menu_docs.rb +85 -0
  43. data/lib/ruby_ui/dialog/dialog_content.rb +2 -3
  44. data/lib/ruby_ui/dialog/dialog_controller.js +1 -1
  45. data/lib/ruby_ui/dialog/dialog_docs.rb +102 -0
  46. data/lib/ruby_ui/docs/base.rb +90 -0
  47. data/lib/ruby_ui/docs/component_setup_tabs.rb +15 -0
  48. data/lib/ruby_ui/docs/components_table.rb +13 -0
  49. data/lib/ruby_ui/docs/header.rb +17 -0
  50. data/lib/ruby_ui/docs/sidebar_examples.rb +22 -0
  51. data/lib/ruby_ui/docs/visual_code_example.rb +22 -0
  52. data/lib/ruby_ui/dropdown_menu/dropdown_menu.rb +9 -0
  53. data/lib/ruby_ui/dropdown_menu/dropdown_menu_content.rb +17 -2
  54. data/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js +43 -14
  55. data/lib/ruby_ui/dropdown_menu/dropdown_menu_docs.rb +212 -0
  56. data/lib/ruby_ui/form/form_docs.rb +178 -0
  57. data/lib/ruby_ui/form/form_field.rb +1 -1
  58. data/lib/ruby_ui/form/form_field_error.rb +1 -1
  59. data/lib/ruby_ui/form/form_field_hint.rb +1 -1
  60. data/lib/ruby_ui/form/form_field_label.rb +7 -1
  61. data/lib/ruby_ui/hover_card/hover_card_docs.rb +71 -0
  62. data/lib/ruby_ui/input/input.rb +9 -1
  63. data/lib/ruby_ui/input/input_docs.rb +68 -0
  64. data/lib/ruby_ui/link/link.rb +32 -16
  65. data/lib/ruby_ui/link/link_docs.rb +106 -0
  66. data/lib/ruby_ui/masked_input/masked_input_docs.rb +47 -0
  67. data/lib/ruby_ui/pagination/pagination_docs.rb +127 -0
  68. data/lib/ruby_ui/popover/popover_docs.rb +971 -0
  69. data/lib/ruby_ui/progress/progress_docs.rb +27 -0
  70. data/lib/ruby_ui/radio_button/radio_button.rb +3 -1
  71. data/lib/ruby_ui/radio_button/radio_button_docs.rb +53 -0
  72. data/lib/ruby_ui/select/select_docs.rb +129 -0
  73. data/lib/ruby_ui/select/select_item.rb +14 -5
  74. data/lib/ruby_ui/select/select_trigger.rb +9 -4
  75. data/lib/ruby_ui/separator/separator_docs.rb +36 -0
  76. data/lib/ruby_ui/sheet/sheet_content.rb +1 -1
  77. data/lib/ruby_ui/sheet/sheet_docs.rb +76 -0
  78. data/lib/ruby_ui/shortcut_key/shortcut_key_docs.rb +29 -0
  79. data/lib/ruby_ui/sidebar/collapsible_sidebar.rb +99 -0
  80. data/lib/ruby_ui/sidebar/mobile_sidebar.rb +45 -0
  81. data/lib/ruby_ui/sidebar/non_collapsible_sidebar.rb +17 -0
  82. data/lib/ruby_ui/sidebar/sidebar.rb +29 -0
  83. data/lib/ruby_ui/sidebar/sidebar_content.rb +20 -0
  84. data/lib/ruby_ui/sidebar/sidebar_controller.js +67 -0
  85. data/lib/ruby_ui/sidebar/sidebar_docs.rb +176 -0
  86. data/lib/ruby_ui/sidebar/sidebar_footer.rb +20 -0
  87. data/lib/ruby_ui/sidebar/sidebar_group.rb +20 -0
  88. data/lib/ruby_ui/sidebar/sidebar_group_action.rb +33 -0
  89. data/lib/ruby_ui/sidebar/sidebar_group_content.rb +20 -0
  90. data/lib/ruby_ui/sidebar/sidebar_group_label.rb +26 -0
  91. data/lib/ruby_ui/sidebar/sidebar_header.rb +20 -0
  92. data/lib/ruby_ui/sidebar/sidebar_input.rb +20 -0
  93. data/lib/ruby_ui/sidebar/sidebar_inset.rb +23 -0
  94. data/lib/ruby_ui/sidebar/sidebar_menu.rb +20 -0
  95. data/lib/ruby_ui/sidebar/sidebar_menu_action.rb +48 -0
  96. data/lib/ruby_ui/sidebar/sidebar_menu_badge.rb +30 -0
  97. data/lib/ruby_ui/sidebar/sidebar_menu_button.rb +63 -0
  98. data/lib/ruby_ui/sidebar/sidebar_menu_item.rb +20 -0
  99. data/lib/ruby_ui/sidebar/sidebar_menu_skeleton.rb +36 -0
  100. data/lib/ruby_ui/sidebar/sidebar_menu_sub.rb +24 -0
  101. data/lib/ruby_ui/sidebar/sidebar_menu_sub_button.rb +50 -0
  102. data/lib/ruby_ui/sidebar/sidebar_menu_sub_item.rb +9 -0
  103. data/lib/ruby_ui/sidebar/sidebar_rail.rb +36 -0
  104. data/lib/ruby_ui/sidebar/sidebar_separator.rb +20 -0
  105. data/lib/ruby_ui/sidebar/sidebar_trigger.rb +42 -0
  106. data/lib/ruby_ui/sidebar/sidebar_wrapper.rb +24 -0
  107. data/lib/ruby_ui/skeleton/skeleton_docs.rb +29 -0
  108. data/lib/ruby_ui/switch/switch.rb +12 -2
  109. data/lib/ruby_ui/switch/switch_docs.rb +46 -0
  110. data/lib/ruby_ui/table/table_docs.rb +102 -0
  111. data/lib/ruby_ui/table/table_footer.rb +1 -1
  112. data/lib/ruby_ui/table/table_row.rb +1 -1
  113. data/lib/ruby_ui/tabs/tabs_docs.rb +211 -0
  114. data/lib/ruby_ui/tabs/tabs_trigger.rb +7 -1
  115. data/lib/ruby_ui/textarea/textarea.rb +8 -1
  116. data/lib/ruby_ui/textarea/textarea_docs.rb +54 -0
  117. data/lib/ruby_ui/theme_toggle/theme_toggle_docs.rb +71 -0
  118. data/lib/ruby_ui/tooltip/tooltip_controller.js +5 -4
  119. data/lib/ruby_ui/tooltip/tooltip_docs.rb +52 -0
  120. data/lib/ruby_ui/typography/typography_docs.rb +107 -0
  121. data/lib/ruby_ui.rb +1 -1
  122. metadata +81 -6
@@ -1,5 +1,11 @@
1
1
  import { Controller } from "@hotwired/stimulus";
2
- import { computePosition, flip, shift, offset } from "@floating-ui/dom";
2
+ import {
3
+ computePosition,
4
+ flip,
5
+ shift,
6
+ offset,
7
+ autoUpdate,
8
+ } from "@floating-ui/dom";
3
9
 
4
10
  export default class extends Controller {
5
11
  static targets = ["trigger", "content", "menuItem"];
@@ -12,17 +18,34 @@ export default class extends Controller {
12
18
  type: Object,
13
19
  default: {},
14
20
  },
15
- }
21
+ };
16
22
 
17
23
  connect() {
18
24
  this.boundHandleKeydown = this.#handleKeydown.bind(this); // Bind the function so we can remove it later
19
25
  this.selectedIndex = -1;
26
+
27
+ this.#setupAutoUpdate();
28
+ }
29
+
30
+ disconnect() {
31
+ if (this.autoUpdateCleanup) {
32
+ this.autoUpdateCleanup();
33
+ }
34
+ }
35
+
36
+ #setupAutoUpdate() {
37
+ this.autoUpdateCleanup = autoUpdate(
38
+ this.triggerTarget,
39
+ this.contentTarget,
40
+ this.#computeTooltip.bind(this),
41
+ );
20
42
  }
21
43
 
22
44
  #computeTooltip() {
23
45
  computePosition(this.triggerTarget, this.contentTarget, {
24
46
  placement: this.optionsValue.placement || "top",
25
47
  middleware: [flip(), shift(), offset(8)],
48
+ strategy: this.optionsValue.strategy || "absolute",
26
49
  }).then(({ x, y }) => {
27
50
  Object.assign(this.contentTarget.style, {
28
51
  left: `${x}px`,
@@ -40,14 +63,16 @@ export default class extends Controller {
40
63
  }
41
64
 
42
65
  toggle() {
43
- this.contentTarget.classList.contains("hidden") ? this.#open() : this.close();
66
+ this.contentTarget.classList.contains("hidden")
67
+ ? this.#open()
68
+ : this.close();
44
69
  }
45
70
 
46
71
  #open() {
47
72
  this.openValue = true;
48
73
  this.#deselectAll();
49
74
  this.#addEventListeners();
50
- this.#computeTooltip()
75
+ this.#computeTooltip();
51
76
  this.contentTarget.classList.remove("hidden");
52
77
  }
53
78
 
@@ -59,15 +84,17 @@ export default class extends Controller {
59
84
 
60
85
  #handleKeydown(e) {
61
86
  // return if no menu items (one line fix for)
62
- if (this.menuItemTargets.length === 0) { return; }
87
+ if (this.menuItemTargets.length === 0) {
88
+ return;
89
+ }
63
90
 
64
- if (e.key === 'ArrowDown') {
91
+ if (e.key === "ArrowDown") {
65
92
  e.preventDefault();
66
93
  this.#updateSelectedItem(1);
67
- } else if (e.key === 'ArrowUp') {
94
+ } else if (e.key === "ArrowUp") {
68
95
  e.preventDefault();
69
96
  this.#updateSelectedItem(-1);
70
- } else if (e.key === 'Enter' && this.selectedIndex !== -1) {
97
+ } else if (e.key === "Enter" && this.selectedIndex !== -1) {
71
98
  e.preventDefault();
72
99
  this.menuItemTargets[this.selectedIndex].click();
73
100
  }
@@ -76,7 +103,7 @@ export default class extends Controller {
76
103
  #updateSelectedItem(direction) {
77
104
  // Check if any of the menuItemTargets have aria-selected="true" and set the selectedIndex to that index
78
105
  this.menuItemTargets.forEach((item, index) => {
79
- if (item.getAttribute('aria-selected') === 'true') {
106
+ if (item.getAttribute("aria-selected") === "true") {
80
107
  this.selectedIndex = index;
81
108
  }
82
109
  });
@@ -99,22 +126,24 @@ export default class extends Controller {
99
126
  #toggleAriaSelected(element, isSelected) {
100
127
  // Add or remove attribute
101
128
  if (isSelected) {
102
- element.setAttribute('aria-selected', 'true');
129
+ element.setAttribute("aria-selected", "true");
103
130
  } else {
104
- element.removeAttribute('aria-selected');
131
+ element.removeAttribute("aria-selected");
105
132
  }
106
133
  }
107
134
 
108
135
  #deselectAll() {
109
- this.menuItemTargets.forEach(item => this.#toggleAriaSelected(item, false));
136
+ this.menuItemTargets.forEach((item) =>
137
+ this.#toggleAriaSelected(item, false),
138
+ );
110
139
  this.selectedIndex = -1;
111
140
  }
112
141
 
113
142
  #addEventListeners() {
114
- document.addEventListener('keydown', this.boundHandleKeydown);
143
+ document.addEventListener("keydown", this.boundHandleKeydown);
115
144
  }
116
145
 
117
146
  #removeEventListeners() {
118
- document.removeEventListener('keydown', this.boundHandleKeydown);
147
+ document.removeEventListener("keydown", this.boundHandleKeydown);
119
148
  }
120
149
  }
@@ -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
@@ -9,7 +9,13 @@ module RubyUI
9
9
  private
10
10
 
11
11
  def default_attrs
12
- {class: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"}
12
+ {
13
+ class: [
14
+ "empty:hidden text-sm font-medium leading-none",
15
+ "peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
16
+ "peer-aria-disabled:cursor-not-allowed peer-aria-disabled:opacity-70 peer-aria-disabled:pointer-events-none"
17
+ ]
18
+ }
13
19
  end
14
20
  end
15
21
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Views::Docs::HoverCard < Views::Base
4
+ def view_template
5
+ component = "HoverCard"
6
+
7
+ div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
8
+ render Docs::Header.new(title: "Hover Card", description: "For sighted users to preview content available behind a link.")
9
+
10
+ Heading(level: 2) { "Usage" }
11
+
12
+ render Docs::VisualCodeExample.new(title: "Example", context: self) do
13
+ <<~RUBY
14
+ HoverCard do
15
+ HoverCardTrigger do
16
+ Button(variant: :link) { "@joeldrapper" } # Make this a link in order to navigate somewhere
17
+ end
18
+ HoverCardContent do
19
+ div(class: "flex justify-between space-x-4") do
20
+ Avatar do
21
+ AvatarImage(src: "https://avatars.githubusercontent.com/u/246692?v=4", alt: "joeldrapper")
22
+ AvatarFallback { "JD" }
23
+ end
24
+ div(class: "space-y-1") do
25
+ h4(class: "text-sm font-medium") { "@joeldrapper" }
26
+ p(class: "text-sm") do
27
+ "Creator of Phlex Components. Ruby on Rails developer."
28
+ end
29
+ div(class: "flex items-center pt-2") do
30
+ svg(
31
+ width: "15",
32
+ height: "15",
33
+ viewbox: "0 0 15 15",
34
+ fill: "none",
35
+ xmlns: "http://www.w3.org/2000/svg",
36
+ class: "mr-2 h-4 w-4 opacity-70"
37
+ ) do |s|
38
+ s.path(
39
+ d:
40
+ "M4.5 1C4.77614 1 5 1.22386 5 1.5V2H10V1.5C10 1.22386 10.2239 1 10.5 1C10.7761 1 11 1.22386 11 1.5V2H12.5C13.3284 2 14 2.67157 14 3.5V12.5C14 13.3284 13.3284 14 12.5 14H2.5C1.67157 14 1 13.3284 1 12.5V3.5C1 2.67157 1.67157 2 2.5 2H4V1.5C4 1.22386 4.22386 1 4.5 1ZM10 3V3.5C10 3.77614 10.2239 4 10.5 4C10.7761 4 11 3.77614 11 3.5V3H12.5C12.7761 3 13 3.22386 13 3.5V5H2V3.5C2 3.22386 2.22386 3 2.5 3H4V3.5C4 3.77614 4.22386 4 4.5 4C4.77614 4 5 3.77614 5 3.5V3H10ZM2 6V12.5C2 12.7761 2.22386 13 2.5 13H12.5C12.7761 13 13 12.7761 13 12.5V6H2ZM7 7.5C7 7.22386 7.22386 7 7.5 7C7.77614 7 8 7.22386 8 7.5C8 7.77614 7.77614 8 7.5 8C7.22386 8 7 7.77614 7 7.5ZM9.5 7C9.22386 7 9 7.22386 9 7.5C9 7.77614 9.22386 8 9.5 8C9.77614 8 10 7.77614 10 7.5C10 7.22386 9.77614 7 9.5 7ZM11 7.5C11 7.22386 11.2239 7 11.5 7C11.7761 7 12 7.22386 12 7.5C12 7.77614 11.7761 8 11.5 8C11.2239 8 11 7.77614 11 7.5ZM11.5 9C11.2239 9 11 9.22386 11 9.5C11 9.77614 11.2239 10 11.5 10C11.7761 10 12 9.77614 12 9.5C12 9.22386 11.7761 9 11.5 9ZM9 9.5C9 9.22386 9.22386 9 9.5 9C9.77614 9 10 9.22386 10 9.5C10 9.77614 9.77614 10 9.5 10C9.22386 10 9 9.77614 9 9.5ZM7.5 9C7.22386 9 7 9.22386 7 9.5C7 9.77614 7.22386 10 7.5 10C7.77614 10 8 9.77614 8 9.5C8 9.22386 7.77614 9 7.5 9ZM5 9.5C5 9.22386 5.22386 9 5.5 9C5.77614 9 6 9.22386 6 9.5C6 9.77614 5.77614 10 5.5 10C5.22386 10 5 9.77614 5 9.5ZM3.5 9C3.22386 9 3 9.22386 3 9.5C3 9.77614 3.22386 10 3.5 10C3.77614 10 4 9.77614 4 9.5C4 9.22386 3.77614 9 3.5 9ZM3 11.5C3 11.2239 3.22386 11 3.5 11C3.77614 11 4 11.2239 4 11.5C4 11.7761 3.77614 12 3.5 12C3.22386 12 3 11.7761 3 11.5ZM5.5 11C5.22386 11 5 11.2239 5 11.5C5 11.7761 5.22386 12 5.5 12C5.77614 12 6 11.7761 6 11.5C6 11.2239 5.77614 11 5.5 11ZM7 11.5C7 11.2239 7.22386 11 7.5 11C7.77614 11 8 11.2239 8 11.5C8 11.7761 7.77614 12 7.5 12C7.22386 12 7 11.7761 7 11.5ZM9.5 11C9.22386 11 9 11.2239 9 11.5C9 11.7761 9.22386 12 9.5 12C9.77614 12 10 11.7761 10 11.5C10 11.2239 9.77614 11 9.5 11Z",
41
+ fill: "currentColor",
42
+ fill_rule: "evenodd",
43
+ clip_rule: "evenodd"
44
+ )
45
+ end
46
+ span(class: "text-xs text-muted-foreground") { "Joined December 2021" }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ RUBY
53
+ end
54
+
55
+ render Components::ComponentSetup::Tabs.new(component_name: component)
56
+
57
+ render Docs::ComponentsTable.new(component_files(component))
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ # def components
64
+ # [
65
+ # Docs::ComponentStruct.new(name: "PopoverController", source: "https://github.com/PhlexUI/phlex_ui_stimulus/blob/main/controllers/popover_controller.js", built_using: :stimulus),
66
+ # Docs::ComponentStruct.new(name: "HoverCard", source: "https://github.com/PhlexUI/phlex_ui/blob/main/lib/phlex_ui/hover_card.rb", built_using: :phlex),
67
+ # Docs::ComponentStruct.new(name: "HoverCardTrigger", source: "https://github.com/PhlexUI/phlex_ui/blob/main/lib/phlex_ui/hover_card/trigger.rb", built_using: :phlex),
68
+ # Docs::ComponentStruct.new(name: "HoverCardContent", source: "https://github.com/PhlexUI/phlex_ui/blob/main/lib/phlex_ui/hover_card/content.rb", built_using: :phlex)
69
+ # ]
70
+ # end
71
+ end
@@ -19,7 +19,15 @@ module RubyUI
19
19
  ruby_ui__form_field_target: "input",
20
20
  action: "input->ruby-ui--form-field#onInput invalid->ruby-ui--form-field#onInvalid"
21
21
  },
22
- class: "flex h-9 w-full rounded-md border bg-background px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50 border-border focus-visible:ring-ring placeholder:text-muted-foreground"
22
+ class: [
23
+ "flex h-9 w-full rounded-md border bg-background px-3 py-1 text-sm shadow-xs transition-[color,box-shadow] border-border ring-0 ring-ring/0",
24
+ "placeholder:text-muted-foreground",
25
+ "disabled:cursor-not-allowed disabled:opacity-50",
26
+ "file:border-0 file:bg-transparent file:text-sm file:font-medium",
27
+ "aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none",
28
+ "focus-visible:outline-none focus-visible:ring-ring/50 focus-visible:ring-2 focus-visible:border-ring focus-visible:shadow-sm",
29
+ (@type.to_s == "file") ? "pt-[7px]" : ""
30
+ ]
23
31
  }
24
32
  end
25
33
  end