ruby_ui 1.0.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +4 -0
- data/lib/generators/ruby_ui/component_generator.rb +5 -1
- data/lib/generators/ruby_ui/dependencies.yml +10 -0
- data/lib/generators/ruby_ui/install/docs_generator.rb +33 -0
- data/lib/generators/ruby_ui/install/install_generator.rb +1 -1
- 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_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_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 +2 -2
- 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 +7 -1
- data/lib/ruby_ui/combobox/combobox_badge.rb +17 -0
- data/lib/ruby_ui/combobox/combobox_badge_trigger.rb +47 -0
- data/lib/ruby_ui/combobox/combobox_checkbox.rb +1 -7
- data/lib/ruby_ui/combobox/combobox_clear_button.rb +40 -0
- data/lib/ruby_ui/combobox/combobox_controller.js +252 -47
- data/lib/ruby_ui/combobox/combobox_docs.rb +286 -0
- data/lib/ruby_ui/combobox/combobox_input_trigger.rb +64 -0
- data/lib/ruby_ui/combobox/combobox_item.rb +5 -7
- data/lib/ruby_ui/combobox/combobox_item_indicator.rb +30 -0
- data/lib/ruby_ui/combobox/combobox_list_group.rb +1 -1
- data/lib/ruby_ui/combobox/combobox_popover.rb +1 -5
- data/lib/ruby_ui/combobox/combobox_radio.rb +1 -8
- data/lib/ruby_ui/combobox/combobox_toggle_all_checkbox.rb +1 -6
- data/lib/ruby_ui/combobox/combobox_trigger.rb +19 -19
- 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/data_table/data_table.rb +29 -0
- data/lib/ruby_ui/data_table/data_table_bulk_actions.rb +18 -0
- data/lib/ruby_ui/data_table/data_table_column_toggle.rb +62 -0
- data/lib/ruby_ui/data_table/data_table_column_visibility_controller.js +14 -0
- data/lib/ruby_ui/data_table/data_table_controller.js +57 -0
- data/lib/ruby_ui/data_table/data_table_docs.rb +180 -0
- data/lib/ruby_ui/data_table/data_table_expand_toggle.rb +53 -0
- data/lib/ruby_ui/data_table/data_table_form.rb +39 -0
- data/lib/ruby_ui/data_table/data_table_kaminari_adapter.rb +17 -0
- data/lib/ruby_ui/data_table/data_table_manual_adapter.rb +17 -0
- data/lib/ruby_ui/data_table/data_table_pagination.rb +100 -0
- data/lib/ruby_ui/data_table/data_table_pagination_bar.rb +15 -0
- data/lib/ruby_ui/data_table/data_table_pagy_adapter.rb +17 -0
- data/lib/ruby_ui/data_table/data_table_per_page_select.rb +35 -0
- data/lib/ruby_ui/data_table/data_table_row_checkbox.rb +30 -0
- data/lib/ruby_ui/data_table/data_table_search.rb +57 -0
- data/lib/ruby_ui/data_table/data_table_search_controller.js +62 -0
- data/lib/ruby_ui/data_table/data_table_select_all_checkbox.rb +21 -0
- data/lib/ruby_ui/data_table/data_table_selection_summary.rb +25 -0
- data/lib/ruby_ui/data_table/data_table_sort_head.rb +112 -0
- data/lib/ruby_ui/data_table/data_table_toolbar.rb +15 -0
- 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_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 +1 -1
- data/lib/ruby_ui/hover_card/hover_card_docs.rb +71 -0
- data/lib/ruby_ui/input/input.rb +4 -3
- data/lib/ruby_ui/input/input_docs.rb +68 -0
- data/lib/ruby_ui/link/link_docs.rb +106 -0
- data/lib/ruby_ui/masked_input/masked_input.rb +11 -1
- data/lib/ruby_ui/masked_input/masked_input_controller.js +13 -0
- data/lib/ruby_ui/masked_input/masked_input_docs.rb +47 -0
- data/lib/ruby_ui/native_select/native_select.rb +39 -0
- data/lib/ruby_ui/native_select/native_select_docs.rb +83 -0
- data/lib/ruby_ui/native_select/native_select_group.rb +15 -0
- data/lib/ruby_ui/native_select/native_select_icon.rb +39 -0
- data/lib/ruby_ui/native_select/native_select_option.rb +15 -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 +1 -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/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/sidebar_docs.rb +176 -0
- data/lib/ruby_ui/skeleton/skeleton_docs.rb +29 -0
- data/lib/ruby_ui/switch/switch_docs.rb +46 -0
- data/lib/ruby_ui/table/table_docs.rb +102 -0
- data/lib/ruby_ui/tabs/tabs_docs.rb +211 -0
- 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_docs.rb +52 -0
- data/lib/ruby_ui/typography/typography_docs.rb +107 -0
- data/lib/ruby_ui.rb +1 -1
- metadata +90 -3
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class ComboboxInputTrigger < Base
|
|
5
|
+
def initialize(placeholder: "", **)
|
|
6
|
+
@placeholder = placeholder
|
|
7
|
+
super(**)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def view_template
|
|
11
|
+
div(**attrs) do
|
|
12
|
+
input(
|
|
13
|
+
type: "text",
|
|
14
|
+
placeholder: @placeholder,
|
|
15
|
+
autocomplete: "off",
|
|
16
|
+
autocorrect: "off",
|
|
17
|
+
spellcheck: "false",
|
|
18
|
+
class: "flex-1 border-0 px-0 bg-transparent outline-none focus:ring-0 placeholder:text-muted-foreground text-sm disabled:cursor-not-allowed",
|
|
19
|
+
data: {
|
|
20
|
+
ruby_ui__combobox_target: "inputTrigger",
|
|
21
|
+
action: "keyup->ruby-ui--combobox#filterItems input->ruby-ui--combobox#filterItems"
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
chevron_icon
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def default_attrs
|
|
31
|
+
{
|
|
32
|
+
class: "flex h-9 w-full items-center rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 aria-invalid:border-destructive",
|
|
33
|
+
data: {
|
|
34
|
+
ruby_ui__combobox_target: "trigger",
|
|
35
|
+
placeholder: @placeholder,
|
|
36
|
+
action: "click->ruby-ui--combobox#openPopover focusin->ruby-ui--combobox#openPopover"
|
|
37
|
+
},
|
|
38
|
+
aria: {
|
|
39
|
+
haspopup: "listbox",
|
|
40
|
+
expanded: "false"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def chevron_icon
|
|
46
|
+
span(class: "shrink-0 flex items-center justify-center size-6 rounded-sm hover:bg-muted hover:text-foreground") do
|
|
47
|
+
svg(
|
|
48
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
49
|
+
width: "24",
|
|
50
|
+
height: "24",
|
|
51
|
+
viewbox: "0 0 24 24",
|
|
52
|
+
fill: "none",
|
|
53
|
+
stroke: "currentColor",
|
|
54
|
+
stroke_width: "2",
|
|
55
|
+
stroke_linecap: "round",
|
|
56
|
+
stroke_linejoin: "round",
|
|
57
|
+
class: "pointer-events-none size-4 text-muted-foreground"
|
|
58
|
+
) do |s|
|
|
59
|
+
s.path(d: "m6 9 6 6 6-6")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -3,19 +3,17 @@
|
|
|
3
3
|
module RubyUI
|
|
4
4
|
class ComboboxItem < Base
|
|
5
5
|
def view_template(&)
|
|
6
|
-
label(**attrs
|
|
6
|
+
label(**attrs) do
|
|
7
|
+
yield if block_given?
|
|
8
|
+
render ComboboxItemIndicator.new
|
|
9
|
+
end
|
|
7
10
|
end
|
|
8
11
|
|
|
9
12
|
private
|
|
10
13
|
|
|
11
14
|
def default_attrs
|
|
12
15
|
{
|
|
13
|
-
class: [
|
|
14
|
-
"flex flex-row w-full text-wrap [&>span,&>div]:truncate gap-2 items-center rounded-sm px-2 py-1 text-sm outline-none cursor-pointer",
|
|
15
|
-
"select-none has-[:checked]:bg-accent hover:bg-accent p-2",
|
|
16
|
-
"[&>svg]:pointer-events-none [&>svg]:size-4 [&>svg]:shrink-0 aria-[current=true]:bg-accent aria-[current=true]:ring aria-[current=true]:ring-offset-2",
|
|
17
|
-
"has-disabled:opacity-50 has-disabled:cursor-not-allowed"
|
|
18
|
-
],
|
|
16
|
+
class: "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground aria-[current=true]:bg-accent has-[input:disabled]:opacity-50 has-[input:disabled]:cursor-not-allowed",
|
|
19
17
|
role: "option",
|
|
20
18
|
data: {
|
|
21
19
|
ruby_ui__combobox_target: "item"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class ComboboxItemIndicator < Base
|
|
5
|
+
def view_template
|
|
6
|
+
svg(
|
|
7
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
8
|
+
width: "24",
|
|
9
|
+
height: "24",
|
|
10
|
+
viewbox: "0 0 24 24",
|
|
11
|
+
fill: "none",
|
|
12
|
+
stroke: "currentColor",
|
|
13
|
+
stroke_width: "2",
|
|
14
|
+
stroke_linecap: "round",
|
|
15
|
+
stroke_linejoin: "round",
|
|
16
|
+
**attrs
|
|
17
|
+
) do |s|
|
|
18
|
+
s.path(d: "M20 6 9 17l-5-5")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def default_attrs
|
|
25
|
+
{
|
|
26
|
+
class: "ml-auto size-4 shrink-0 opacity-0 peer-checked:opacity-100"
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -12,15 +12,11 @@ module RubyUI
|
|
|
12
12
|
{
|
|
13
13
|
class: "inset-auto m-0 absolute border bg-background shadow-lg rounded-lg",
|
|
14
14
|
role: "popover",
|
|
15
|
-
autofocus: true,
|
|
16
15
|
popover: true,
|
|
17
16
|
data: {
|
|
18
17
|
ruby_ui__combobox_target: "popover",
|
|
19
18
|
action: %w[
|
|
20
|
-
|
|
21
|
-
keydown.up->ruby-ui--combobox#keyUpPressed
|
|
22
|
-
keydown.enter->ruby-ui--combobox#keyEnterPressed
|
|
23
|
-
keydown.esc->ruby-ui--combobox#closeDialog:prevent
|
|
19
|
+
toggle->ruby-ui--combobox#handlePopoverToggle
|
|
24
20
|
resize@window->ruby-ui--combobox#updatePopoverWidth
|
|
25
21
|
]
|
|
26
22
|
}
|
|
@@ -10,14 +10,7 @@ module RubyUI
|
|
|
10
10
|
|
|
11
11
|
def default_attrs
|
|
12
12
|
{
|
|
13
|
-
class:
|
|
14
|
-
"aspect-square h-4 w-4 rounded-full border border-primary accent-primary text-primary shadow",
|
|
15
|
-
"focus:outline-none",
|
|
16
|
-
"focus-visible:ring-1 focus-visible:ring-ring",
|
|
17
|
-
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
18
|
-
"checked:bg-primary checked:text-primary-foreground",
|
|
19
|
-
"aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none"
|
|
20
|
-
],
|
|
13
|
+
class: "peer sr-only",
|
|
21
14
|
data: {
|
|
22
15
|
ruby_ui__combobox_target: "input",
|
|
23
16
|
ruby_ui__form_field_target: "input",
|
|
@@ -10,12 +10,7 @@ module RubyUI
|
|
|
10
10
|
|
|
11
11
|
def default_attrs
|
|
12
12
|
{
|
|
13
|
-
class:
|
|
14
|
-
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background accent-primary",
|
|
15
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
16
|
-
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
17
|
-
"aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none"
|
|
18
|
-
],
|
|
13
|
+
class: "peer sr-only disabled:cursor-not-allowed",
|
|
19
14
|
data: {
|
|
20
15
|
ruby_ui__combobox_target: "toggleAll",
|
|
21
16
|
action: "change->ruby-ui--combobox#toggleAllItems"
|
|
@@ -9,7 +9,7 @@ module RubyUI
|
|
|
9
9
|
|
|
10
10
|
def view_template
|
|
11
11
|
button(**attrs) do
|
|
12
|
-
span(class: "truncate", data: {ruby_ui__combobox_target: "triggerContent"}) do
|
|
12
|
+
span(class: "truncate text-muted-foreground", data: {ruby_ui__combobox_target: "triggerContent"}) do
|
|
13
13
|
@placeholder
|
|
14
14
|
end
|
|
15
15
|
icon
|
|
@@ -26,12 +26,13 @@ module RubyUI
|
|
|
26
26
|
"hover:bg-accent hover:text-accent-foreground",
|
|
27
27
|
"disabled:pointer-events-none disabled:opacity-50",
|
|
28
28
|
"aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:cursor-not-allowed",
|
|
29
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
|
29
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
30
|
+
"aria-invalid:border-destructive"
|
|
30
31
|
],
|
|
31
32
|
data: {
|
|
32
33
|
placeholder: @placeholder,
|
|
33
34
|
ruby_ui__combobox_target: "trigger",
|
|
34
|
-
action: "ruby-ui--combobox#openPopover"
|
|
35
|
+
action: "click->ruby-ui--combobox#togglePopover focus->ruby-ui--combobox#openPopover"
|
|
35
36
|
},
|
|
36
37
|
aria: {
|
|
37
38
|
haspopup: "listbox",
|
|
@@ -41,22 +42,21 @@ module RubyUI
|
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
def icon
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
45
|
+
span(class: "shrink-0 flex items-center justify-center size-6 rounded-sm hover:bg-muted hover:text-foreground") do
|
|
46
|
+
svg(
|
|
47
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
48
|
+
width: "24",
|
|
49
|
+
height: "24",
|
|
50
|
+
viewbox: "0 0 24 24",
|
|
51
|
+
fill: "none",
|
|
52
|
+
stroke: "currentColor",
|
|
53
|
+
stroke_width: "2",
|
|
54
|
+
stroke_linecap: "round",
|
|
55
|
+
stroke_linejoin: "round",
|
|
56
|
+
class: "pointer-events-none size-4 text-muted-foreground"
|
|
57
|
+
) do |s|
|
|
58
|
+
s.path(d: "m6 9 6 6 6-6")
|
|
59
|
+
end
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Views::Docs::Command < Views::Base
|
|
4
|
+
def view_template
|
|
5
|
+
component = "Command"
|
|
6
|
+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
|
|
7
|
+
render Docs::Header.new(title: "Command", description: "Fast, composable, unstyled command menu for Phlex.")
|
|
8
|
+
|
|
9
|
+
Heading(level: 2) { "Usage" }
|
|
10
|
+
|
|
11
|
+
render Docs::VisualCodeExample.new(title: "Example", context: self) do
|
|
12
|
+
<<~RUBY
|
|
13
|
+
CommandDialog do
|
|
14
|
+
CommandDialogTrigger do
|
|
15
|
+
Button(variant: "outline", class: 'w-56 pr-2 pl-3 justify-between') do
|
|
16
|
+
div(class: "flex items-center space-x-1") do
|
|
17
|
+
search_icon
|
|
18
|
+
span(class: "text-muted-foreground font-normal") do
|
|
19
|
+
plain "Search"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
ShortcutKey do
|
|
23
|
+
span(class: "text-xs") { "⌘" }
|
|
24
|
+
plain "K"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
CommandDialogContent do
|
|
29
|
+
Command do
|
|
30
|
+
CommandInput(placeholder: "Type a command or search...")
|
|
31
|
+
CommandEmpty { "No results found." }
|
|
32
|
+
CommandList do
|
|
33
|
+
CommandGroup(title: "Components") do
|
|
34
|
+
components_list.each do |component|
|
|
35
|
+
CommandItem(value: component[:name], href: component[:path]) do
|
|
36
|
+
default_icon
|
|
37
|
+
plain component[:name]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
CommandGroup(title: "Settings") do
|
|
42
|
+
settings_list.each do |setting|
|
|
43
|
+
CommandItem(value: setting[:name], href: setting[:path]) do
|
|
44
|
+
default_icon
|
|
45
|
+
plain setting[:name]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
RUBY
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
render Docs::VisualCodeExample.new(title: "With keybinding", context: self) do
|
|
57
|
+
<<~RUBY
|
|
58
|
+
CommandDialog do
|
|
59
|
+
CommandDialogTrigger(keybindings: ['keydown.ctrl+j@window', 'keydown.meta+j@window']) do
|
|
60
|
+
p(class: "text-sm text-muted-foreground") do
|
|
61
|
+
span(class: 'mr-1') { "Press" }
|
|
62
|
+
ShortcutKey do
|
|
63
|
+
span(class: "text-xs") { "⌘" }
|
|
64
|
+
plain "J"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
CommandDialogContent do
|
|
69
|
+
Command do
|
|
70
|
+
CommandInput(placeholder: "Type a command or search...")
|
|
71
|
+
CommandEmpty { "No results found." }
|
|
72
|
+
CommandList do
|
|
73
|
+
CommandGroup(title: "Components") do
|
|
74
|
+
components_list.each do |component|
|
|
75
|
+
CommandItem(value: component[:name], href: component[:path]) do
|
|
76
|
+
default_icon
|
|
77
|
+
plain component[:name]
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
CommandGroup(title: "Settings") do
|
|
82
|
+
settings_list.each do |setting|
|
|
83
|
+
CommandItem(value: setting[:name], href: setting[:path]) do
|
|
84
|
+
default_icon
|
|
85
|
+
plain setting[:name]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
RUBY
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
97
|
+
|
|
98
|
+
render Docs::ComponentsTable.new(component_files(component))
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def search_icon
|
|
105
|
+
svg(
|
|
106
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
107
|
+
viewbox: "0 0 20 20",
|
|
108
|
+
fill: "currentColor",
|
|
109
|
+
class: "w-4 h-4 mr-1.5"
|
|
110
|
+
) do |s|
|
|
111
|
+
s.path(
|
|
112
|
+
fill_rule: "evenodd",
|
|
113
|
+
d:
|
|
114
|
+
"M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z",
|
|
115
|
+
clip_rule: "evenodd"
|
|
116
|
+
)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def default_icon
|
|
121
|
+
svg(
|
|
122
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
123
|
+
viewbox: "0 0 24 24",
|
|
124
|
+
fill: "currentColor",
|
|
125
|
+
class: "w-5 h-5"
|
|
126
|
+
) do |s|
|
|
127
|
+
s.path(
|
|
128
|
+
fill_rule: "evenodd",
|
|
129
|
+
d:
|
|
130
|
+
"M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm4.28 10.28a.75.75 0 000-1.06l-3-3a.75.75 0 10-1.06 1.06l1.72 1.72H8.25a.75.75 0 000 1.5h5.69l-1.72 1.72a.75.75 0 101.06 1.06l3-3z",
|
|
131
|
+
clip_rule: "evenodd"
|
|
132
|
+
)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def components_list
|
|
137
|
+
[
|
|
138
|
+
{name: "Accordion", path: docs_accordion_path},
|
|
139
|
+
{name: "Alert", path: docs_alert_path},
|
|
140
|
+
{name: "Alert Dialog", path: docs_alert_dialog_path},
|
|
141
|
+
{name: "Aspect Ratio", path: docs_aspect_ratio_path},
|
|
142
|
+
{name: "Avatar", path: docs_avatar_path},
|
|
143
|
+
{name: "Badge", path: docs_badge_path}
|
|
144
|
+
]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def settings_list
|
|
148
|
+
[
|
|
149
|
+
{name: "Profile", path: "#"},
|
|
150
|
+
{name: "Mail", path: "#"},
|
|
151
|
+
{name: "Settings", path: "#"}
|
|
152
|
+
]
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Views::Docs::ContextMenu < Views::Base
|
|
4
|
+
def view_template
|
|
5
|
+
component = "ContextMenu"
|
|
6
|
+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
|
|
7
|
+
render Docs::Header.new(title: "Context Menu", description: "Displays a menu to the user — such as a set of actions or functions — triggered by a right click.")
|
|
8
|
+
|
|
9
|
+
Heading(level: 2) { "Usage" }
|
|
10
|
+
|
|
11
|
+
render Docs::VisualCodeExample.new(title: "Example", context: self) do
|
|
12
|
+
<<~RUBY
|
|
13
|
+
ContextMenu do
|
|
14
|
+
ContextMenuTrigger(class: 'flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm') { "Right click here" }
|
|
15
|
+
ContextMenuContent(class: 'w-64') do
|
|
16
|
+
ContextMenuItem(href: '#', shortcut: "⌘[") { "Back" }
|
|
17
|
+
ContextMenuItem(href: '#', shortcut: "⌘]", disabled: true) { "Forward" }
|
|
18
|
+
ContextMenuItem(href: '#', shortcut: "⌘R") { "Reload" }
|
|
19
|
+
ContextMenuSeparator
|
|
20
|
+
ContextMenuItem(href: '#', shortcut: "⌘⇧B", checked: true) { "Show Bookmarks Bar" }
|
|
21
|
+
ContextMenuItem(href: '#') { "Show Full URLs" }
|
|
22
|
+
ContextMenuSeparator
|
|
23
|
+
ContextMenuLabel(inset: true) { "More Tools" }
|
|
24
|
+
ContextMenuSeparator
|
|
25
|
+
ContextMenuItem(href: '#') { "Developer Tools" }
|
|
26
|
+
ContextMenuItem(href: '#') { "Task Manager" }
|
|
27
|
+
ContextMenuItem(href: '#') { "Extensions" }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
RUBY
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
render Docs::VisualCodeExample.new(title: "Placement", context: self) do
|
|
34
|
+
<<~RUBY
|
|
35
|
+
div(class: 'space-y-4') do
|
|
36
|
+
ContextMenu(options: { placement: 'right' }) do
|
|
37
|
+
ContextMenuTrigger(class: 'flex flex-col items-center gap-y-2 h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm') do
|
|
38
|
+
plain "Right click here"
|
|
39
|
+
Badge(variant: :primary) { "right" }
|
|
40
|
+
end
|
|
41
|
+
ContextMenuContent(class: 'w-64') do
|
|
42
|
+
ContextMenuItem(href: '#', shortcut: "⌘[") { "Back" }
|
|
43
|
+
ContextMenuItem(href: '#', shortcut: "⌘]", disabled: true) { "Forward" }
|
|
44
|
+
ContextMenuItem(href: '#', shortcut: "⌘R") { "Reload" }
|
|
45
|
+
ContextMenuSeparator
|
|
46
|
+
ContextMenuItem(href: '#', shortcut: "⌘⇧B", checked: true) { "Show Bookmarks Bar" }
|
|
47
|
+
ContextMenuItem(href: '#') { "Show Full URLs" }
|
|
48
|
+
ContextMenuSeparator
|
|
49
|
+
ContextMenuLabel(inset: true) { "More Tools" }
|
|
50
|
+
ContextMenuSeparator
|
|
51
|
+
ContextMenuItem(href: '#') { "Developer Tools" }
|
|
52
|
+
ContextMenuItem(href: '#') { "Task Manager" }
|
|
53
|
+
ContextMenuItem(href: '#') { "Extensions" }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
ContextMenu(options: { placement: 'left' }) do
|
|
57
|
+
ContextMenuTrigger(class: 'flex flex-col items-center gap-y-2 h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm') do
|
|
58
|
+
plain "Right click here"
|
|
59
|
+
Badge(variant: :primary) { "left" }
|
|
60
|
+
end
|
|
61
|
+
ContextMenuContent(class: 'w-64') do
|
|
62
|
+
ContextMenuItem(href: '#', shortcut: "⌘[") { "Back" }
|
|
63
|
+
ContextMenuItem(href: '#', shortcut: "⌘]", disabled: true) { "Forward" }
|
|
64
|
+
ContextMenuItem(href: '#', shortcut: "⌘R") { "Reload" }
|
|
65
|
+
ContextMenuSeparator
|
|
66
|
+
ContextMenuItem(href: '#', shortcut: "⌘⇧B", checked: true) { "Show Bookmarks Bar" }
|
|
67
|
+
ContextMenuItem(href: '#') { "Show Full URLs" }
|
|
68
|
+
ContextMenuSeparator
|
|
69
|
+
ContextMenuLabel(inset: true) { "More Tools" }
|
|
70
|
+
ContextMenuSeparator
|
|
71
|
+
ContextMenuItem(href: '#') { "Developer Tools" }
|
|
72
|
+
ContextMenuItem(href: '#') { "Task Manager" }
|
|
73
|
+
ContextMenuItem(href: '#') { "Extensions" }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
RUBY
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
81
|
+
|
|
82
|
+
render Docs::ComponentsTable.new(component_files(component))
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class DataTable < Base
|
|
5
|
+
register_element :turbo_frame, tag: "turbo-frame"
|
|
6
|
+
|
|
7
|
+
def initialize(id:, **attrs)
|
|
8
|
+
@id = id
|
|
9
|
+
super(**attrs)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def view_template(&block)
|
|
13
|
+
turbo_frame(id: @id, target: "_top") do
|
|
14
|
+
div(**attrs) do
|
|
15
|
+
yield if block
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def default_attrs
|
|
23
|
+
{
|
|
24
|
+
class: "w-full space-y-4",
|
|
25
|
+
data: {controller: "ruby-ui--data-table"}
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class DataTableBulkActions < Base
|
|
5
|
+
def view_template(&)
|
|
6
|
+
div(**attrs, &)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def default_attrs
|
|
12
|
+
{
|
|
13
|
+
class: "hidden items-center gap-2",
|
|
14
|
+
data: {"ruby-ui--data-table-target": "bulkActions"}
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class DataTableColumnToggle < Base
|
|
5
|
+
def initialize(columns:, **attrs)
|
|
6
|
+
@columns = columns
|
|
7
|
+
super(**attrs)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def view_template
|
|
11
|
+
div(**attrs) do
|
|
12
|
+
render RubyUI::DropdownMenu.new do
|
|
13
|
+
render RubyUI::DropdownMenuTrigger.new do
|
|
14
|
+
render RubyUI::Button.new(variant: :outline, size: :sm) do
|
|
15
|
+
plain "Columns"
|
|
16
|
+
# inline chevron-down SVG (lucide 24px, 1px stroke)
|
|
17
|
+
svg(
|
|
18
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
19
|
+
width: "16",
|
|
20
|
+
height: "16",
|
|
21
|
+
viewBox: "0 0 24 24",
|
|
22
|
+
fill: "none",
|
|
23
|
+
stroke: "currentColor",
|
|
24
|
+
stroke_width: "2",
|
|
25
|
+
stroke_linecap: "round",
|
|
26
|
+
stroke_linejoin: "round",
|
|
27
|
+
class: "w-4 h-4 ml-1"
|
|
28
|
+
) do |s|
|
|
29
|
+
s.polyline(points: "6 9 12 15 18 9")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
render RubyUI::DropdownMenuContent.new do
|
|
34
|
+
@columns.each do |col|
|
|
35
|
+
label(class: "flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm cursor-pointer hover:bg-accent") do
|
|
36
|
+
input(
|
|
37
|
+
type: "checkbox",
|
|
38
|
+
checked: true,
|
|
39
|
+
class: "h-4 w-4 rounded border border-input accent-primary cursor-pointer",
|
|
40
|
+
data: {
|
|
41
|
+
column_key: col[:key].to_s,
|
|
42
|
+
action: "change->ruby-ui--data-table-column-visibility#toggle"
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
span { plain col[:label] }
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def default_attrs
|
|
56
|
+
{
|
|
57
|
+
class: "relative",
|
|
58
|
+
data: {controller: "ruby-ui--data-table-column-visibility"}
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// app/javascript/controllers/ruby_ui/data_table_column_visibility_controller.js
|
|
2
|
+
import { Controller } from "@hotwired/stimulus";
|
|
3
|
+
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
toggle(event) {
|
|
6
|
+
const key = event.target.dataset.columnKey;
|
|
7
|
+
const visible = event.target.checked;
|
|
8
|
+
const root = this.element.closest('[data-controller~="ruby-ui--data-table"]');
|
|
9
|
+
if (!root) return;
|
|
10
|
+
root
|
|
11
|
+
.querySelectorAll(`[data-column="${key}"]`)
|
|
12
|
+
.forEach((el) => el.classList.toggle("hidden", !visible));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// app/javascript/controllers/ruby_ui/data_table_controller.js
|
|
2
|
+
import { Controller } from "@hotwired/stimulus";
|
|
3
|
+
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = [
|
|
6
|
+
"selectAll",
|
|
7
|
+
"rowCheckbox",
|
|
8
|
+
"selectionSummary",
|
|
9
|
+
"selectionBar",
|
|
10
|
+
"bulkActions",
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
connect() {
|
|
14
|
+
this.updateState();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
toggleAll(event) {
|
|
18
|
+
const checked = event.target.checked;
|
|
19
|
+
this.rowCheckboxTargets.forEach((cb) => {
|
|
20
|
+
cb.checked = checked;
|
|
21
|
+
});
|
|
22
|
+
this.updateState();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
toggleRow() {
|
|
26
|
+
this.updateState();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
toggleRowDetail(event) {
|
|
30
|
+
const button = event.currentTarget;
|
|
31
|
+
const id = button.getAttribute("aria-controls");
|
|
32
|
+
if (!id) return;
|
|
33
|
+
const target = document.getElementById(id);
|
|
34
|
+
if (!target) return;
|
|
35
|
+
const expanded = button.getAttribute("aria-expanded") === "true";
|
|
36
|
+
button.setAttribute("aria-expanded", String(!expanded));
|
|
37
|
+
target.classList.toggle("hidden", expanded);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
updateState() {
|
|
41
|
+
const total = this.rowCheckboxTargets.length;
|
|
42
|
+
const selected = this.rowCheckboxTargets.filter((cb) => cb.checked).length;
|
|
43
|
+
|
|
44
|
+
if (this.hasSelectAllTarget) {
|
|
45
|
+
this.selectAllTarget.checked = total > 0 && selected === total;
|
|
46
|
+
this.selectAllTarget.indeterminate = selected > 0 && selected < total;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (this.hasSelectionSummaryTarget) {
|
|
50
|
+
this.selectionSummaryTarget.textContent = `${selected} of ${total} row(s) selected.`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (this.hasBulkActionsTarget) {
|
|
54
|
+
this.bulkActionsTarget.classList.toggle("hidden", selected === 0);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|