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
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Views::Docs::Progress < Views::Base
|
|
4
|
+
def view_template
|
|
5
|
+
component = "Progress"
|
|
6
|
+
|
|
7
|
+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
|
|
8
|
+
render Docs::Header.new(title: "Progress", description: "Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.")
|
|
9
|
+
|
|
10
|
+
render Docs::VisualCodeExample.new(title: "Example", context: self) do
|
|
11
|
+
<<~RUBY
|
|
12
|
+
Progress(value: 50, class: "w-[60%]")
|
|
13
|
+
RUBY
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
render Docs::VisualCodeExample.new(title: "With custom indicator color", context: self) do
|
|
17
|
+
<<~RUBY
|
|
18
|
+
Progress(value: 35, class: "w-[60%] [&>*]:bg-success")
|
|
19
|
+
RUBY
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
23
|
+
|
|
24
|
+
render Docs::ComponentsTable.new(component_files(component))
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -17,7 +17,9 @@ module RubyUI
|
|
|
17
17
|
},
|
|
18
18
|
class: [
|
|
19
19
|
"h-4 w-4 p-0 border-primary rounded-full flex-none",
|
|
20
|
-
"disabled:cursor-not-allowed disabled:opacity-50"
|
|
20
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
21
|
+
"checked:bg-primary checked:text-primary-foreground dark:checked:bg-secondary checked:text-primary checked:border-primary",
|
|
22
|
+
"aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none"
|
|
21
23
|
]
|
|
22
24
|
}
|
|
23
25
|
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Views::Docs::RadioButton < Views::Base
|
|
4
|
+
def view_template
|
|
5
|
+
component = "RadioButton"
|
|
6
|
+
|
|
7
|
+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
|
|
8
|
+
render Docs::Header.new(title: "Radio Button", description: "A control that allows users to make a single selection from a list of options.")
|
|
9
|
+
|
|
10
|
+
Heading(level: 2) { "Usage" }
|
|
11
|
+
|
|
12
|
+
render Docs::VisualCodeExample.new(title: "Example", context: self) do
|
|
13
|
+
<<~RUBY
|
|
14
|
+
div(class: "flex items-center space-x-2") do
|
|
15
|
+
RadioButton(id: "default")
|
|
16
|
+
FormFieldLabel(for: "default") { "Default" }
|
|
17
|
+
end
|
|
18
|
+
RUBY
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
render Docs::VisualCodeExample.new(title: "Checked", context: self) do
|
|
22
|
+
<<~RUBY
|
|
23
|
+
div(class: "flex items-center space-x-2") do
|
|
24
|
+
RadioButton(id: "checked", checked: true)
|
|
25
|
+
FormFieldLabel(for: "checked") { "Checked" }
|
|
26
|
+
end
|
|
27
|
+
RUBY
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
render Docs::VisualCodeExample.new(title: "Disabled", context: self) do
|
|
31
|
+
<<~RUBY
|
|
32
|
+
div(class: "flex flex-row items-center gap-2") do
|
|
33
|
+
RadioButton(class: "peer",id: "disabled", disabled: true)
|
|
34
|
+
FormFieldLabel(for: "disabled") { "Disabled" }
|
|
35
|
+
end
|
|
36
|
+
RUBY
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
render Docs::VisualCodeExample.new(title: "Aria Disabled", context: self) do
|
|
40
|
+
<<~RUBY
|
|
41
|
+
div(class: "flex flex-row items-center gap-2") do
|
|
42
|
+
RadioButton(class: "peer", id: "aria-disabled", aria: {disabled: "true"})
|
|
43
|
+
FormFieldLabel(for: "aria-disabled") { "Aria Disabled" }
|
|
44
|
+
end
|
|
45
|
+
RUBY
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
49
|
+
|
|
50
|
+
render Docs::ComponentsTable.new(component_files(component))
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Views::Docs::Select < Views::Base
|
|
4
|
+
def view_template
|
|
5
|
+
component = "Select"
|
|
6
|
+
|
|
7
|
+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
|
|
8
|
+
render Docs::Header.new(title: "Select", description: "Displays a list of options for the user to pick from—triggered by a button.")
|
|
9
|
+
|
|
10
|
+
Heading(level: 2) { "Usage" }
|
|
11
|
+
|
|
12
|
+
render Docs::VisualCodeExample.new(title: "Select (Deconstructed)", context: self) do
|
|
13
|
+
<<~RUBY
|
|
14
|
+
Select(class: "w-56") do
|
|
15
|
+
SelectInput(value: "apple", id: "select-a-fruit")
|
|
16
|
+
|
|
17
|
+
SelectTrigger do
|
|
18
|
+
SelectValue(placeholder: "Select a fruit", id: "select-a-fruit") { "Apple" }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
SelectContent(outlet_id: "select-a-fruit") do
|
|
22
|
+
SelectGroup do
|
|
23
|
+
SelectLabel { "Fruits" }
|
|
24
|
+
SelectItem(value: "apple") { "Apple" }
|
|
25
|
+
SelectItem(value: "orange") { "Orange" }
|
|
26
|
+
SelectItem(value: "banana") { "Banana" }
|
|
27
|
+
SelectItem(value: "watermelon") { "Watermelon" }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
RUBY
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
render Docs::VisualCodeExample.new(title: "Pre-selected Item", context: self) do
|
|
35
|
+
<<~RUBY
|
|
36
|
+
Select(class: "w-56") do
|
|
37
|
+
SelectInput(value: "banana", id: "select-preselected-fruit")
|
|
38
|
+
|
|
39
|
+
SelectTrigger do
|
|
40
|
+
SelectValue(placeholder: "Select a fruit", id: "select-preselected-fruit") { "Banana" }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
SelectContent(outlet_id: "select-preselected-fruit") do
|
|
44
|
+
SelectGroup do
|
|
45
|
+
SelectLabel { "Fruits" }
|
|
46
|
+
SelectItem(value: "apple") { "Apple" }
|
|
47
|
+
SelectItem(value: "orange") { "Orange" }
|
|
48
|
+
SelectItem(value: "banana") { "Banana" }
|
|
49
|
+
SelectItem(value: "watermelon") { "Watermelon" }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
RUBY
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
render Docs::VisualCodeExample.new(title: "Disabled", context: self) do
|
|
57
|
+
<<~RUBY
|
|
58
|
+
Select(class: "w-56") do
|
|
59
|
+
SelectInput(value: "apple", id: "select-a-fruit")
|
|
60
|
+
|
|
61
|
+
SelectTrigger(disabled: true) do
|
|
62
|
+
SelectValue(placeholder: "Select a fruit", id: "select-a-fruit") { "Apple" }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
RUBY
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
render Docs::VisualCodeExample.new(title: "Data Disabled", context: self) do
|
|
69
|
+
<<~RUBY
|
|
70
|
+
Select(class: "w-56") do
|
|
71
|
+
SelectInput(value: "apple", id: "select-a-fruit")
|
|
72
|
+
|
|
73
|
+
SelectTrigger do
|
|
74
|
+
SelectValue(placeholder: "Select a fruit", id: "select-a-fruit") { "Apple" }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
SelectContent(outlet_id: "select-a-fruit") do
|
|
78
|
+
SelectGroup do
|
|
79
|
+
SelectLabel { "Fruits" }
|
|
80
|
+
SelectItem(data: {disabled: true}, value: "apple") { "Apple" }
|
|
81
|
+
SelectItem(value: "orange") { "Orange" }
|
|
82
|
+
SelectItem(value: "banana") { "Banana" }
|
|
83
|
+
SelectItem(data: {disabled: true}, value: "watermelon") { "Watermelon" }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
RUBY
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
render Docs::VisualCodeExample.new(title: "Aria Disabled Trigger", context: self) do
|
|
91
|
+
<<~RUBY
|
|
92
|
+
Select(class: "w-56") do
|
|
93
|
+
SelectInput(value: "apple", id: "select-a-fruit")
|
|
94
|
+
|
|
95
|
+
SelectTrigger(aria: {disabled: "true"}) do
|
|
96
|
+
SelectValue(placeholder: "Select a fruit", id: "select-a-fruit") { "Apple" }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
RUBY
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
render Docs::VisualCodeExample.new(title: "Aria Disabled Item", context: self) do
|
|
103
|
+
<<~RUBY
|
|
104
|
+
Select(class: "w-56") do
|
|
105
|
+
SelectInput(value: "apple", id: "select-a-fruit")
|
|
106
|
+
|
|
107
|
+
SelectTrigger do
|
|
108
|
+
SelectValue(placeholder: "Select a fruit", id: "select-a-fruit") { "Apple" }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
SelectContent(outlet_id: "select-a-fruit") do
|
|
112
|
+
SelectGroup do
|
|
113
|
+
SelectLabel { "Fruits" }
|
|
114
|
+
SelectItem(aria: {disabled: "true"}, value: "apple") { "Apple" }
|
|
115
|
+
SelectItem(value: "orange") { "Orange" }
|
|
116
|
+
SelectItem(value: "banana") { "Banana" }
|
|
117
|
+
SelectItem(aria: {disabled: "true"}, value: "watermelon") { "Watermelon" }
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
RUBY
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
125
|
+
|
|
126
|
+
render Docs::ComponentsTable.new(component_files(component))
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -37,15 +37,24 @@ module RubyUI
|
|
|
37
37
|
{
|
|
38
38
|
role: "option",
|
|
39
39
|
tabindex: "0",
|
|
40
|
-
|
|
40
|
+
data_value: @value,
|
|
41
|
+
aria_selected: "false",
|
|
42
|
+
data_orientation: "vertical",
|
|
43
|
+
class: [
|
|
44
|
+
"item group relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors",
|
|
45
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
46
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
47
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
48
|
+
"aria-selected:bg-accent aria-selected:text-accent-foreground",
|
|
49
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
50
|
+
"aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:cursor-not-allowed"
|
|
51
|
+
],
|
|
41
52
|
data: {
|
|
42
53
|
controller: "ruby-ui--select-item",
|
|
43
54
|
action: "click->ruby-ui--select#selectItem keydown.enter->ruby-ui--select#selectItem keydown.down->ruby-ui--select#handleKeyDown keydown.up->ruby-ui--select#handleKeyUp keydown.esc->ruby-ui--select#handleEsc",
|
|
44
55
|
ruby_ui__select_target: "item"
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
data_orientation: "vertical",
|
|
48
|
-
aria_selected: "false"
|
|
56
|
+
}
|
|
57
|
+
|
|
49
58
|
}
|
|
50
59
|
end
|
|
51
60
|
end
|
|
@@ -33,12 +33,12 @@ module RubyUI
|
|
|
33
33
|
|
|
34
34
|
def default_attrs
|
|
35
35
|
{
|
|
36
|
+
type: "button",
|
|
37
|
+
role: "combobox",
|
|
36
38
|
data: {
|
|
37
39
|
action: "ruby-ui--select#onClick",
|
|
38
40
|
ruby_ui__select_target: "trigger"
|
|
39
41
|
},
|
|
40
|
-
type: "button",
|
|
41
|
-
role: "combobox",
|
|
42
42
|
aria: {
|
|
43
43
|
controls: "radix-:r0:",
|
|
44
44
|
expanded: "false",
|
|
@@ -46,8 +46,13 @@ module RubyUI
|
|
|
46
46
|
haspopup: "listbox",
|
|
47
47
|
activedescendant: true
|
|
48
48
|
},
|
|
49
|
-
class:
|
|
50
|
-
"truncate w-full flex h-9 items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background
|
|
49
|
+
class: [
|
|
50
|
+
"truncate w-full flex h-9 items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background",
|
|
51
|
+
"placeholder:text-muted-foreground",
|
|
52
|
+
"focus:outline-none focus:ring-1 focus:ring-ring",
|
|
53
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
54
|
+
"aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none"
|
|
55
|
+
]
|
|
51
56
|
}
|
|
52
57
|
end
|
|
53
58
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Views::Docs::Separator < Views::Base
|
|
4
|
+
def view_template
|
|
5
|
+
component = "Separator"
|
|
6
|
+
|
|
7
|
+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
|
|
8
|
+
render Docs::Header.new(title: "Separator", description: "Visually or semantically separates content.")
|
|
9
|
+
|
|
10
|
+
Heading(level: 2) { "Usage" }
|
|
11
|
+
|
|
12
|
+
render Docs::VisualCodeExample.new(title: "Example", context: self) do
|
|
13
|
+
<<~RUBY
|
|
14
|
+
div do
|
|
15
|
+
div(class: "space-y-1") do
|
|
16
|
+
h4(class: "text-sm font-medium leading-none") { "RubyUI" }
|
|
17
|
+
p(class: "text-sm text-muted-foreground") { "An open-source UI component library." }
|
|
18
|
+
end
|
|
19
|
+
Separator(class: "my-4")
|
|
20
|
+
div(class: "flex h-5 items-center space-x-4 text-sm") do
|
|
21
|
+
div { "Blog" }
|
|
22
|
+
Separator(as: :hr, orientation: :vertical)
|
|
23
|
+
div { "Docs" }
|
|
24
|
+
Separator(orientation: :vertical)
|
|
25
|
+
div { "Source" }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
RUBY
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
32
|
+
|
|
33
|
+
render Docs::ComponentsTable.new(component_files(component))
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -33,7 +33,7 @@ module RubyUI
|
|
|
33
33
|
{
|
|
34
34
|
data_state: "open", # For animate in
|
|
35
35
|
class: [
|
|
36
|
-
"fixed pointer-events-auto z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
|
36
|
+
"fixed pointer-events-auto z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 overflow-scroll",
|
|
37
37
|
@side_classes
|
|
38
38
|
]
|
|
39
39
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Views::Docs::Sheet < Views::Base
|
|
4
|
+
def view_template
|
|
5
|
+
component = "Sheet"
|
|
6
|
+
|
|
7
|
+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
|
|
8
|
+
render Docs::Header.new(title: "Sheet", description: "Extends the Sheet component to display content that complements the main content of the screen.")
|
|
9
|
+
|
|
10
|
+
Heading(level: 2) { "Usage" }
|
|
11
|
+
|
|
12
|
+
render Docs::VisualCodeExample.new(title: "Example", context: self) do
|
|
13
|
+
<<~RUBY
|
|
14
|
+
Sheet do
|
|
15
|
+
SheetTrigger do
|
|
16
|
+
Button(variant: :outline) { "Open Sheet" }
|
|
17
|
+
end
|
|
18
|
+
SheetContent(class: 'sm:max-w-sm') do
|
|
19
|
+
SheetHeader do
|
|
20
|
+
SheetTitle { "Edit profile" }
|
|
21
|
+
SheetDescription { "Make changes to your profile here. Click save when you're done." }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
SheetMiddle do
|
|
25
|
+
label { "Name" }
|
|
26
|
+
Input(placeholder: "Joel Drapper") { "Joel Drapper" }
|
|
27
|
+
label { "Email" }
|
|
28
|
+
Input(placeholder: "joel@drapper.me")
|
|
29
|
+
end
|
|
30
|
+
SheetFooter do
|
|
31
|
+
Button(variant: :outline, data: { action: 'click->ruby-ui--sheet-content#close' }) { "Cancel" }
|
|
32
|
+
Button(type: "submit") { "Save" }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
RUBY
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
render Docs::VisualCodeExample.new(title: "Side", description: "Use the side property to indicate the edge of the screen where the component will appear.", context: self) do
|
|
40
|
+
<<~RUBY
|
|
41
|
+
div(class: 'grid grid-cols-2 gap-4') do
|
|
42
|
+
# -- TOP --
|
|
43
|
+
Sheet do
|
|
44
|
+
SheetTrigger do
|
|
45
|
+
Button(variant: :outline, class: 'w-full justify-center') { :top }
|
|
46
|
+
end
|
|
47
|
+
SheetContent(side: :top, class: ("sm:max-w-sm" if [:left, :right].include?(:top))) do
|
|
48
|
+
SheetHeader do
|
|
49
|
+
SheetTitle { "Edit profile" }
|
|
50
|
+
SheetDescription { "Make changes to your profile here. Click save when you're done." }
|
|
51
|
+
end
|
|
52
|
+
Form do
|
|
53
|
+
SheetMiddle do
|
|
54
|
+
label { "Name" }
|
|
55
|
+
Input(placeholder: "Joel Drapper") { "Joel Drapper" }
|
|
56
|
+
|
|
57
|
+
label { "Email" }
|
|
58
|
+
Input(placeholder: "joel@drapper.me")
|
|
59
|
+
end
|
|
60
|
+
SheetFooter do
|
|
61
|
+
Button(variant: :outline, data: { action: 'click->ruby-ui--sheet-content#close' }) { "Cancel" }
|
|
62
|
+
Button(type: "submit") { "Save" }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
RUBY
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
72
|
+
|
|
73
|
+
render Docs::ComponentsTable.new(component_files(component))
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Views::Docs::ShortcutKey < Views::Base
|
|
4
|
+
def view_template
|
|
5
|
+
component = "ShortcutKey"
|
|
6
|
+
|
|
7
|
+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
|
|
8
|
+
render Docs::Header.new(title: "Shortcut Key", description: "A component for displaying keyboard shortcuts.")
|
|
9
|
+
|
|
10
|
+
Heading(level: 2) { "Usage" }
|
|
11
|
+
|
|
12
|
+
render Docs::VisualCodeExample.new(title: "Example", context: self) do
|
|
13
|
+
<<~RUBY
|
|
14
|
+
div(class: "flex flex-col items-center gap-y-4") do
|
|
15
|
+
ShortcutKey do
|
|
16
|
+
span(class: "text-xs") { "⌘" }
|
|
17
|
+
plain "K"
|
|
18
|
+
end
|
|
19
|
+
p(class: "text-muted-foreground text-sm text-center") { "Note this does not trigger anything, it is purely a visual prompt" }
|
|
20
|
+
end
|
|
21
|
+
RUBY
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
25
|
+
|
|
26
|
+
render Docs::ComponentsTable.new(component_files(component))
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class CollapsibleSidebar < Base
|
|
5
|
+
def initialize(side: :left, variant: :sidebar, collapsible: :offcanvas, open: true, **attrs)
|
|
6
|
+
@side = side
|
|
7
|
+
@variant = variant
|
|
8
|
+
@collapsible = collapsible
|
|
9
|
+
@open = open
|
|
10
|
+
super(**attrs)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template(&)
|
|
14
|
+
MobileSidebar(side: @side, **attrs, &)
|
|
15
|
+
div(**mix(sidebar_attrs, attrs)) do
|
|
16
|
+
div(**gap_element_attrs)
|
|
17
|
+
div(**content_wrapper_attrs) do
|
|
18
|
+
div(**content_attrs, &)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def sidebar_attrs
|
|
26
|
+
{
|
|
27
|
+
class: "group peer hidden text-sidebar-foreground md:block",
|
|
28
|
+
data: {
|
|
29
|
+
state: @open ? "expanded" : "collapsed",
|
|
30
|
+
collapsible: @open ? "" : @collapsible,
|
|
31
|
+
variant: @variant,
|
|
32
|
+
side: @side,
|
|
33
|
+
collapsible_kind: @collapsible,
|
|
34
|
+
ruby_ui__sidebar_target: "sidebar"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def gap_element_attrs
|
|
40
|
+
{
|
|
41
|
+
class: [
|
|
42
|
+
"relative w-[var(--sidebar-width)] bg-transparent transition-[width]",
|
|
43
|
+
"duration-200 ease-linear",
|
|
44
|
+
"group-data-[collapsible=offcanvas]:w-0",
|
|
45
|
+
"group-data-[side=right]:rotate-180",
|
|
46
|
+
variant_classes
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def content_wrapper_attrs
|
|
52
|
+
{
|
|
53
|
+
class: [
|
|
54
|
+
"fixed inset-y-0 z-10 hidden h-svh w-[var(--sidebar-width)]",
|
|
55
|
+
"transition-[left,right,width] duration-200 ease-linear md:flex",
|
|
56
|
+
content_wrapper_side_classes,
|
|
57
|
+
content_wrapper_variant_classes
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def content_attrs
|
|
63
|
+
{
|
|
64
|
+
class: [
|
|
65
|
+
"flex h-full w-full flex-col bg-sidebar",
|
|
66
|
+
"group-data-[variant=floating]:rounded-lg",
|
|
67
|
+
"group-data-[variant=floating]:border",
|
|
68
|
+
"group-data-[variant=floating]:border-sidebar-border",
|
|
69
|
+
"group-data-[variant=floating]:shadow"
|
|
70
|
+
],
|
|
71
|
+
data: {
|
|
72
|
+
sidebar: "sidebar"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def variant_classes
|
|
78
|
+
if %i[floating inset].include?(@variant)
|
|
79
|
+
"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
|
|
80
|
+
else
|
|
81
|
+
"group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)]"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def content_wrapper_side_classes
|
|
86
|
+
return "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" if @side == :left
|
|
87
|
+
|
|
88
|
+
"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def content_wrapper_variant_classes
|
|
92
|
+
if %i[floating inset].include?(@variant)
|
|
93
|
+
"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
|
|
94
|
+
else
|
|
95
|
+
"group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)] group-data-[side=left]:border-r group-data-[side=right]:border-l"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class MobileSidebar < Base
|
|
5
|
+
SIDEBAR_WIDTH_MOBILE = "18rem"
|
|
6
|
+
|
|
7
|
+
def initialize(side: :left, **attrs)
|
|
8
|
+
@side = side
|
|
9
|
+
super(**attrs)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def view_template(&)
|
|
13
|
+
Sheet(**attrs) do
|
|
14
|
+
SheetContent(
|
|
15
|
+
side: @side,
|
|
16
|
+
class: "w-[var(--sidebar-width)] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden",
|
|
17
|
+
style: {
|
|
18
|
+
"--sidebar-width": SIDEBAR_WIDTH_MOBILE
|
|
19
|
+
},
|
|
20
|
+
data: {
|
|
21
|
+
sidebar: "sidebar",
|
|
22
|
+
mobile: "true"
|
|
23
|
+
}
|
|
24
|
+
) do
|
|
25
|
+
SheetHeader(class: "sr-only") do
|
|
26
|
+
SheetTitle { "Sidebar" }
|
|
27
|
+
SheetDescription { "Displays the mobile sidebar." }
|
|
28
|
+
end
|
|
29
|
+
div(class: "flex h-full w-full flex-col", &)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def default_attrs
|
|
37
|
+
{
|
|
38
|
+
data: {
|
|
39
|
+
ruby_ui__sidebar_target: "mobileSidebar",
|
|
40
|
+
action: "ruby--ui-sidebar:open->ruby-ui--sheet#open:self"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class NonCollapsibleSidebar < Base
|
|
5
|
+
def view_template(&)
|
|
6
|
+
div(**attrs, &)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def default_attrs
|
|
12
|
+
{
|
|
13
|
+
class: "flex h-full w-[var(--sidebar-width)] flex-col bg-sidebar text-sidebar-foreground"
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class Sidebar < Base
|
|
5
|
+
SIDES = %i[left right].freeze
|
|
6
|
+
VARIANTS = %i[sidebar floating inset].freeze
|
|
7
|
+
COLLAPSIBLES = %i[offcanvas icon none].freeze
|
|
8
|
+
|
|
9
|
+
def initialize(side: :left, variant: :sidebar, collapsible: :offcanvas, open: true, **attrs)
|
|
10
|
+
raise ArgumentError, "Invalid side: #{side}." unless SIDES.include?(side.to_sym)
|
|
11
|
+
raise ArgumentError "Invalid variant: #{variant}." unless VARIANTS.include?(variant.to_sym)
|
|
12
|
+
raise ArgumentError, "Invalid collapsible: #{collapsible}." unless COLLAPSIBLES.include?(collapsible.to_sym)
|
|
13
|
+
|
|
14
|
+
@side = side.to_sym
|
|
15
|
+
@variant = variant.to_sym
|
|
16
|
+
@collapsible = collapsible.to_sym
|
|
17
|
+
@open = open
|
|
18
|
+
super(**attrs)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def view_template(&)
|
|
22
|
+
if @collapsible == :none
|
|
23
|
+
NonCollapsibleSidebar(**attrs, &)
|
|
24
|
+
else
|
|
25
|
+
CollapsibleSidebar(side: @side, variant: @variant, collapsible: @collapsible, open: @open, **attrs, &)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class SidebarContent < Base
|
|
5
|
+
def view_template(&)
|
|
6
|
+
div(**attrs, &)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def default_attrs
|
|
12
|
+
{
|
|
13
|
+
class: "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
|
|
14
|
+
data: {
|
|
15
|
+
sidebar: "content"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|