ruby_ui 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +6 -0
  4. data/lib/generators/ruby_ui/component/all_generator.rb +22 -0
  5. data/lib/generators/ruby_ui/component_generator.rb +4 -3
  6. data/lib/generators/ruby_ui/install/docs_generator.rb +33 -0
  7. data/lib/generators/ruby_ui/install/install_generator.rb +1 -7
  8. data/lib/generators/ruby_ui/install/templates/tailwind.css.erb +0 -3
  9. data/lib/generators/ruby_ui/javascript_utils.rb +4 -0
  10. data/lib/ruby_ui/accordion/accordion_docs.rb +53 -0
  11. data/lib/ruby_ui/alert/alert_docs.rb +135 -0
  12. data/lib/ruby_ui/alert_dialog/alert_dialog_content.rb +2 -3
  13. data/lib/ruby_ui/alert_dialog/alert_dialog_docs.rb +35 -0
  14. data/lib/ruby_ui/aspect_ratio/aspect_ratio_docs.rb +64 -0
  15. data/lib/ruby_ui/avatar/avatar_docs.rb +92 -0
  16. data/lib/ruby_ui/badge/badge_docs.rb +80 -0
  17. data/lib/ruby_ui/breadcrumb/breadcrumb_docs.rb +116 -0
  18. data/lib/ruby_ui/button/button.rb +32 -16
  19. data/lib/ruby_ui/button/button_docs.rb +143 -0
  20. data/lib/ruby_ui/calendar/calendar_docs.rb +34 -0
  21. data/lib/ruby_ui/card/card_docs.rb +114 -0
  22. data/lib/ruby_ui/carousel/carousel_docs.rb +104 -0
  23. data/lib/ruby_ui/chart/chart_docs.rb +115 -0
  24. data/lib/ruby_ui/checkbox/checkbox.rb +7 -1
  25. data/lib/ruby_ui/checkbox/checkbox_docs.rb +41 -0
  26. data/lib/ruby_ui/clipboard/clipboard_docs.rb +30 -0
  27. data/lib/ruby_ui/codeblock/codeblock_docs.rb +55 -0
  28. data/lib/ruby_ui/collapsible/collapsible_docs.rb +96 -0
  29. data/lib/ruby_ui/combobox/combobox.rb +3 -2
  30. data/lib/ruby_ui/combobox/combobox_checkbox.rb +4 -2
  31. data/lib/ruby_ui/combobox/combobox_controller.js +20 -5
  32. data/lib/ruby_ui/combobox/combobox_docs.rb +151 -0
  33. data/lib/ruby_ui/combobox/combobox_item.rb +2 -1
  34. data/lib/ruby_ui/combobox/combobox_popover.rb +2 -1
  35. data/lib/ruby_ui/combobox/combobox_radio.rb +8 -1
  36. data/lib/ruby_ui/combobox/combobox_search_input.rb +11 -5
  37. data/lib/ruby_ui/combobox/combobox_toggle_all_checkbox.rb +4 -2
  38. data/lib/ruby_ui/combobox/combobox_trigger.rb +8 -2
  39. data/lib/ruby_ui/command/command_controller.js +0 -1
  40. data/lib/ruby_ui/command/command_docs.rb +154 -0
  41. data/lib/ruby_ui/context_menu/context_menu.rb +1 -1
  42. data/lib/ruby_ui/context_menu/context_menu_docs.rb +85 -0
  43. data/lib/ruby_ui/dialog/dialog_content.rb +2 -3
  44. data/lib/ruby_ui/dialog/dialog_controller.js +1 -1
  45. data/lib/ruby_ui/dialog/dialog_docs.rb +102 -0
  46. data/lib/ruby_ui/docs/base.rb +90 -0
  47. data/lib/ruby_ui/docs/component_setup_tabs.rb +15 -0
  48. data/lib/ruby_ui/docs/components_table.rb +13 -0
  49. data/lib/ruby_ui/docs/header.rb +17 -0
  50. data/lib/ruby_ui/docs/sidebar_examples.rb +22 -0
  51. data/lib/ruby_ui/docs/visual_code_example.rb +22 -0
  52. data/lib/ruby_ui/dropdown_menu/dropdown_menu.rb +9 -0
  53. data/lib/ruby_ui/dropdown_menu/dropdown_menu_content.rb +17 -2
  54. data/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js +43 -14
  55. data/lib/ruby_ui/dropdown_menu/dropdown_menu_docs.rb +212 -0
  56. data/lib/ruby_ui/form/form_docs.rb +178 -0
  57. data/lib/ruby_ui/form/form_field.rb +1 -1
  58. data/lib/ruby_ui/form/form_field_error.rb +1 -1
  59. data/lib/ruby_ui/form/form_field_hint.rb +1 -1
  60. data/lib/ruby_ui/form/form_field_label.rb +7 -1
  61. data/lib/ruby_ui/hover_card/hover_card_docs.rb +71 -0
  62. data/lib/ruby_ui/input/input.rb +9 -1
  63. data/lib/ruby_ui/input/input_docs.rb +68 -0
  64. data/lib/ruby_ui/link/link.rb +32 -16
  65. data/lib/ruby_ui/link/link_docs.rb +106 -0
  66. data/lib/ruby_ui/masked_input/masked_input_docs.rb +47 -0
  67. data/lib/ruby_ui/pagination/pagination_docs.rb +127 -0
  68. data/lib/ruby_ui/popover/popover_docs.rb +971 -0
  69. data/lib/ruby_ui/progress/progress_docs.rb +27 -0
  70. data/lib/ruby_ui/radio_button/radio_button.rb +3 -1
  71. data/lib/ruby_ui/radio_button/radio_button_docs.rb +53 -0
  72. data/lib/ruby_ui/select/select_docs.rb +129 -0
  73. data/lib/ruby_ui/select/select_item.rb +14 -5
  74. data/lib/ruby_ui/select/select_trigger.rb +9 -4
  75. data/lib/ruby_ui/separator/separator_docs.rb +36 -0
  76. data/lib/ruby_ui/sheet/sheet_content.rb +1 -1
  77. data/lib/ruby_ui/sheet/sheet_docs.rb +76 -0
  78. data/lib/ruby_ui/shortcut_key/shortcut_key_docs.rb +29 -0
  79. data/lib/ruby_ui/sidebar/collapsible_sidebar.rb +99 -0
  80. data/lib/ruby_ui/sidebar/mobile_sidebar.rb +45 -0
  81. data/lib/ruby_ui/sidebar/non_collapsible_sidebar.rb +17 -0
  82. data/lib/ruby_ui/sidebar/sidebar.rb +29 -0
  83. data/lib/ruby_ui/sidebar/sidebar_content.rb +20 -0
  84. data/lib/ruby_ui/sidebar/sidebar_controller.js +67 -0
  85. data/lib/ruby_ui/sidebar/sidebar_docs.rb +176 -0
  86. data/lib/ruby_ui/sidebar/sidebar_footer.rb +20 -0
  87. data/lib/ruby_ui/sidebar/sidebar_group.rb +20 -0
  88. data/lib/ruby_ui/sidebar/sidebar_group_action.rb +33 -0
  89. data/lib/ruby_ui/sidebar/sidebar_group_content.rb +20 -0
  90. data/lib/ruby_ui/sidebar/sidebar_group_label.rb +26 -0
  91. data/lib/ruby_ui/sidebar/sidebar_header.rb +20 -0
  92. data/lib/ruby_ui/sidebar/sidebar_input.rb +20 -0
  93. data/lib/ruby_ui/sidebar/sidebar_inset.rb +23 -0
  94. data/lib/ruby_ui/sidebar/sidebar_menu.rb +20 -0
  95. data/lib/ruby_ui/sidebar/sidebar_menu_action.rb +48 -0
  96. data/lib/ruby_ui/sidebar/sidebar_menu_badge.rb +30 -0
  97. data/lib/ruby_ui/sidebar/sidebar_menu_button.rb +63 -0
  98. data/lib/ruby_ui/sidebar/sidebar_menu_item.rb +20 -0
  99. data/lib/ruby_ui/sidebar/sidebar_menu_skeleton.rb +36 -0
  100. data/lib/ruby_ui/sidebar/sidebar_menu_sub.rb +24 -0
  101. data/lib/ruby_ui/sidebar/sidebar_menu_sub_button.rb +50 -0
  102. data/lib/ruby_ui/sidebar/sidebar_menu_sub_item.rb +9 -0
  103. data/lib/ruby_ui/sidebar/sidebar_rail.rb +36 -0
  104. data/lib/ruby_ui/sidebar/sidebar_separator.rb +20 -0
  105. data/lib/ruby_ui/sidebar/sidebar_trigger.rb +42 -0
  106. data/lib/ruby_ui/sidebar/sidebar_wrapper.rb +24 -0
  107. data/lib/ruby_ui/skeleton/skeleton_docs.rb +29 -0
  108. data/lib/ruby_ui/switch/switch.rb +12 -2
  109. data/lib/ruby_ui/switch/switch_docs.rb +46 -0
  110. data/lib/ruby_ui/table/table_docs.rb +102 -0
  111. data/lib/ruby_ui/table/table_footer.rb +1 -1
  112. data/lib/ruby_ui/table/table_row.rb +1 -1
  113. data/lib/ruby_ui/tabs/tabs_docs.rb +211 -0
  114. data/lib/ruby_ui/tabs/tabs_trigger.rb +7 -1
  115. data/lib/ruby_ui/textarea/textarea.rb +8 -1
  116. data/lib/ruby_ui/textarea/textarea_docs.rb +54 -0
  117. data/lib/ruby_ui/theme_toggle/theme_toggle_docs.rb +71 -0
  118. data/lib/ruby_ui/tooltip/tooltip_controller.js +5 -4
  119. data/lib/ruby_ui/tooltip/tooltip_docs.rb +52 -0
  120. data/lib/ruby_ui/typography/typography_docs.rb +107 -0
  121. data/lib/ruby_ui.rb +1 -1
  122. metadata +81 -6
@@ -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
- class: "item group relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
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
- data_value: @value,
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 placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
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