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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +6 -0
- data/lib/generators/ruby_ui/component/all_generator.rb +22 -0
- data/lib/generators/ruby_ui/component_generator.rb +4 -3
- data/lib/generators/ruby_ui/install/docs_generator.rb +33 -0
- data/lib/generators/ruby_ui/install/install_generator.rb +1 -7
- data/lib/generators/ruby_ui/install/templates/tailwind.css.erb +0 -3
- data/lib/generators/ruby_ui/javascript_utils.rb +4 -0
- data/lib/ruby_ui/accordion/accordion_docs.rb +53 -0
- data/lib/ruby_ui/alert/alert_docs.rb +135 -0
- data/lib/ruby_ui/alert_dialog/alert_dialog_content.rb +2 -3
- data/lib/ruby_ui/alert_dialog/alert_dialog_docs.rb +35 -0
- data/lib/ruby_ui/aspect_ratio/aspect_ratio_docs.rb +64 -0
- data/lib/ruby_ui/avatar/avatar_docs.rb +92 -0
- data/lib/ruby_ui/badge/badge_docs.rb +80 -0
- data/lib/ruby_ui/breadcrumb/breadcrumb_docs.rb +116 -0
- data/lib/ruby_ui/button/button.rb +32 -16
- data/lib/ruby_ui/button/button_docs.rb +143 -0
- data/lib/ruby_ui/calendar/calendar_docs.rb +34 -0
- data/lib/ruby_ui/card/card_docs.rb +114 -0
- data/lib/ruby_ui/carousel/carousel_docs.rb +104 -0
- data/lib/ruby_ui/chart/chart_docs.rb +115 -0
- data/lib/ruby_ui/checkbox/checkbox.rb +7 -1
- data/lib/ruby_ui/checkbox/checkbox_docs.rb +41 -0
- data/lib/ruby_ui/clipboard/clipboard_docs.rb +30 -0
- data/lib/ruby_ui/codeblock/codeblock_docs.rb +55 -0
- data/lib/ruby_ui/collapsible/collapsible_docs.rb +96 -0
- data/lib/ruby_ui/combobox/combobox.rb +3 -2
- data/lib/ruby_ui/combobox/combobox_checkbox.rb +4 -2
- data/lib/ruby_ui/combobox/combobox_controller.js +20 -5
- data/lib/ruby_ui/combobox/combobox_docs.rb +151 -0
- data/lib/ruby_ui/combobox/combobox_item.rb +2 -1
- data/lib/ruby_ui/combobox/combobox_popover.rb +2 -1
- data/lib/ruby_ui/combobox/combobox_radio.rb +8 -1
- data/lib/ruby_ui/combobox/combobox_search_input.rb +11 -5
- data/lib/ruby_ui/combobox/combobox_toggle_all_checkbox.rb +4 -2
- data/lib/ruby_ui/combobox/combobox_trigger.rb +8 -2
- data/lib/ruby_ui/command/command_controller.js +0 -1
- data/lib/ruby_ui/command/command_docs.rb +154 -0
- data/lib/ruby_ui/context_menu/context_menu.rb +1 -1
- data/lib/ruby_ui/context_menu/context_menu_docs.rb +85 -0
- data/lib/ruby_ui/dialog/dialog_content.rb +2 -3
- data/lib/ruby_ui/dialog/dialog_controller.js +1 -1
- data/lib/ruby_ui/dialog/dialog_docs.rb +102 -0
- data/lib/ruby_ui/docs/base.rb +90 -0
- data/lib/ruby_ui/docs/component_setup_tabs.rb +15 -0
- data/lib/ruby_ui/docs/components_table.rb +13 -0
- data/lib/ruby_ui/docs/header.rb +17 -0
- data/lib/ruby_ui/docs/sidebar_examples.rb +22 -0
- data/lib/ruby_ui/docs/visual_code_example.rb +22 -0
- data/lib/ruby_ui/dropdown_menu/dropdown_menu.rb +9 -0
- data/lib/ruby_ui/dropdown_menu/dropdown_menu_content.rb +17 -2
- data/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js +43 -14
- data/lib/ruby_ui/dropdown_menu/dropdown_menu_docs.rb +212 -0
- data/lib/ruby_ui/form/form_docs.rb +178 -0
- data/lib/ruby_ui/form/form_field.rb +1 -1
- data/lib/ruby_ui/form/form_field_error.rb +1 -1
- data/lib/ruby_ui/form/form_field_hint.rb +1 -1
- data/lib/ruby_ui/form/form_field_label.rb +7 -1
- data/lib/ruby_ui/hover_card/hover_card_docs.rb +71 -0
- data/lib/ruby_ui/input/input.rb +9 -1
- data/lib/ruby_ui/input/input_docs.rb +68 -0
- data/lib/ruby_ui/link/link.rb +32 -16
- data/lib/ruby_ui/link/link_docs.rb +106 -0
- data/lib/ruby_ui/masked_input/masked_input_docs.rb +47 -0
- data/lib/ruby_ui/pagination/pagination_docs.rb +127 -0
- data/lib/ruby_ui/popover/popover_docs.rb +971 -0
- data/lib/ruby_ui/progress/progress_docs.rb +27 -0
- data/lib/ruby_ui/radio_button/radio_button.rb +3 -1
- data/lib/ruby_ui/radio_button/radio_button_docs.rb +53 -0
- data/lib/ruby_ui/select/select_docs.rb +129 -0
- data/lib/ruby_ui/select/select_item.rb +14 -5
- data/lib/ruby_ui/select/select_trigger.rb +9 -4
- data/lib/ruby_ui/separator/separator_docs.rb +36 -0
- data/lib/ruby_ui/sheet/sheet_content.rb +1 -1
- data/lib/ruby_ui/sheet/sheet_docs.rb +76 -0
- data/lib/ruby_ui/shortcut_key/shortcut_key_docs.rb +29 -0
- data/lib/ruby_ui/sidebar/collapsible_sidebar.rb +99 -0
- data/lib/ruby_ui/sidebar/mobile_sidebar.rb +45 -0
- data/lib/ruby_ui/sidebar/non_collapsible_sidebar.rb +17 -0
- data/lib/ruby_ui/sidebar/sidebar.rb +29 -0
- data/lib/ruby_ui/sidebar/sidebar_content.rb +20 -0
- data/lib/ruby_ui/sidebar/sidebar_controller.js +67 -0
- data/lib/ruby_ui/sidebar/sidebar_docs.rb +176 -0
- data/lib/ruby_ui/sidebar/sidebar_footer.rb +20 -0
- data/lib/ruby_ui/sidebar/sidebar_group.rb +20 -0
- data/lib/ruby_ui/sidebar/sidebar_group_action.rb +33 -0
- data/lib/ruby_ui/sidebar/sidebar_group_content.rb +20 -0
- data/lib/ruby_ui/sidebar/sidebar_group_label.rb +26 -0
- data/lib/ruby_ui/sidebar/sidebar_header.rb +20 -0
- data/lib/ruby_ui/sidebar/sidebar_input.rb +20 -0
- data/lib/ruby_ui/sidebar/sidebar_inset.rb +23 -0
- data/lib/ruby_ui/sidebar/sidebar_menu.rb +20 -0
- data/lib/ruby_ui/sidebar/sidebar_menu_action.rb +48 -0
- data/lib/ruby_ui/sidebar/sidebar_menu_badge.rb +30 -0
- data/lib/ruby_ui/sidebar/sidebar_menu_button.rb +63 -0
- data/lib/ruby_ui/sidebar/sidebar_menu_item.rb +20 -0
- data/lib/ruby_ui/sidebar/sidebar_menu_skeleton.rb +36 -0
- data/lib/ruby_ui/sidebar/sidebar_menu_sub.rb +24 -0
- data/lib/ruby_ui/sidebar/sidebar_menu_sub_button.rb +50 -0
- data/lib/ruby_ui/sidebar/sidebar_menu_sub_item.rb +9 -0
- data/lib/ruby_ui/sidebar/sidebar_rail.rb +36 -0
- data/lib/ruby_ui/sidebar/sidebar_separator.rb +20 -0
- data/lib/ruby_ui/sidebar/sidebar_trigger.rb +42 -0
- data/lib/ruby_ui/sidebar/sidebar_wrapper.rb +24 -0
- data/lib/ruby_ui/skeleton/skeleton_docs.rb +29 -0
- data/lib/ruby_ui/switch/switch.rb +12 -2
- data/lib/ruby_ui/switch/switch_docs.rb +46 -0
- data/lib/ruby_ui/table/table_docs.rb +102 -0
- data/lib/ruby_ui/table/table_footer.rb +1 -1
- data/lib/ruby_ui/table/table_row.rb +1 -1
- data/lib/ruby_ui/tabs/tabs_docs.rb +211 -0
- data/lib/ruby_ui/tabs/tabs_trigger.rb +7 -1
- data/lib/ruby_ui/textarea/textarea.rb +8 -1
- data/lib/ruby_ui/textarea/textarea_docs.rb +54 -0
- data/lib/ruby_ui/theme_toggle/theme_toggle_docs.rb +71 -0
- data/lib/ruby_ui/tooltip/tooltip_controller.js +5 -4
- data/lib/ruby_ui/tooltip/tooltip_docs.rb +52 -0
- data/lib/ruby_ui/typography/typography_docs.rb +107 -0
- data/lib/ruby_ui.rb +1 -1
- metadata +81 -6
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Controller } from "@hotwired/stimulus";
|
|
2
|
-
import {
|
|
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")
|
|
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) {
|
|
87
|
+
if (this.menuItemTargets.length === 0) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
63
90
|
|
|
64
|
-
if (e.key ===
|
|
91
|
+
if (e.key === "ArrowDown") {
|
|
65
92
|
e.preventDefault();
|
|
66
93
|
this.#updateSelectedItem(1);
|
|
67
|
-
} else if (e.key ===
|
|
94
|
+
} else if (e.key === "ArrowUp") {
|
|
68
95
|
e.preventDefault();
|
|
69
96
|
this.#updateSelectedItem(-1);
|
|
70
|
-
} else if (e.key ===
|
|
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(
|
|
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(
|
|
129
|
+
element.setAttribute("aria-selected", "true");
|
|
103
130
|
} else {
|
|
104
|
-
element.removeAttribute(
|
|
131
|
+
element.removeAttribute("aria-selected");
|
|
105
132
|
}
|
|
106
133
|
}
|
|
107
134
|
|
|
108
135
|
#deselectAll() {
|
|
109
|
-
this.menuItemTargets.forEach(item =>
|
|
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(
|
|
143
|
+
document.addEventListener("keydown", this.boundHandleKeydown);
|
|
115
144
|
}
|
|
116
145
|
|
|
117
146
|
#removeEventListeners() {
|
|
118
|
-
document.removeEventListener(
|
|
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
|
|
@@ -9,7 +9,13 @@ module RubyUI
|
|
|
9
9
|
private
|
|
10
10
|
|
|
11
11
|
def default_attrs
|
|
12
|
-
{
|
|
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
|
data/lib/ruby_ui/input/input.rb
CHANGED
|
@@ -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:
|
|
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
|