ruby_ui 1.3.0 → 1.4.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/README.md +6 -0
- data/lib/generators/ruby_ui/component/all_generator.rb +6 -4
- data/lib/generators/ruby_ui/component_generator.rb +51 -31
- data/lib/ruby_ui/accordion/accordion_content.rb +4 -2
- data/lib/ruby_ui/accordion/accordion_controller.js +19 -4
- data/lib/ruby_ui/avatar/avatar_image.rb +5 -1
- data/lib/ruby_ui/data_table/data_table_docs.rb +1 -1
- data/lib/ruby_ui/dialog/dialog_content.rb +7 -19
- data/lib/ruby_ui/dialog/dialog_controller.js +22 -10
- data/lib/ruby_ui/dropdown_menu/dropdown_menu_item.rb +10 -4
- data/lib/ruby_ui/form/form_docs.rb +89 -0
- data/lib/ruby_ui/table/table_docs.rb +2 -2
- data/lib/ruby_ui/tabs/tabs_docs.rb +1 -1
- data/lib/ruby_ui/tabs/tabs_trigger.rb +10 -4
- data/lib/ruby_ui.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8dd00d73934f8ff9e2ac4e92994de33c910192f92df634def1e1726bde86393
|
|
4
|
+
data.tar.gz: 50ba081c5c25aa85581620726935ca1469b402751eade75cdb8adbc2042160d5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 44150fd27560c0b3a7d5193501abfd755e36a15ae74dc555088b9a30a1b228adc521dd8a82fc705e3c2396a45bb0baf48aed1351840d96aaa87eae6a9c2d545d
|
|
7
|
+
data.tar.gz: fc536b32afeabc805ad4815ce9be98090caf2402e6c5cc920011d9166b721690eb35938195b5079bf44b7400c4ec5454642f00ea844df22062ef326e32316bc4
|
data/README.md
CHANGED
|
@@ -55,6 +55,12 @@ You can generate your components using `ruby_ui:component` generator.
|
|
|
55
55
|
bin/rails g ruby_ui:component Accordion
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
You can also generate multiple components at once.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
bin/rails g ruby_ui:component Button Link Input Textarea
|
|
62
|
+
```
|
|
63
|
+
|
|
58
64
|
You also can generate all components using `ruby_ui:component:all` generator
|
|
59
65
|
|
|
60
66
|
## Documentation 📖
|
|
@@ -10,11 +10,13 @@ module RubyUI
|
|
|
10
10
|
def generate_components
|
|
11
11
|
say "Generating all components..."
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
# Each component lives in its own directory; select directories only so stray
|
|
14
|
+
# files (e.g. base.rb or a macOS .DS_Store) are never passed as component names.
|
|
15
|
+
folder_names = Dir.children(self.class.source_root).select do |entry|
|
|
16
|
+
File.directory?(File.join(self.class.source_root, entry))
|
|
17
17
|
end
|
|
18
|
+
|
|
19
|
+
run "bin/rails generate ruby_ui:component #{folder_names.join(" ")} --force #{options["force"]}"
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
end
|
|
@@ -7,69 +7,89 @@ module RubyUI
|
|
|
7
7
|
namespace "ruby_ui:component"
|
|
8
8
|
|
|
9
9
|
source_root File.expand_path("../../ruby_ui", __dir__)
|
|
10
|
-
argument :
|
|
10
|
+
argument :component_names, type: :array, required: true, banner: "Button Link Input"
|
|
11
11
|
class_option :force, type: :boolean, default: false
|
|
12
12
|
class_option :with_docs, type: :boolean, default: false
|
|
13
13
|
|
|
14
|
-
def
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
def generate_components
|
|
15
|
+
validate_components!
|
|
16
|
+
|
|
17
|
+
component_names.each do |component_name|
|
|
18
|
+
say "Generating #{component_name} files..."
|
|
19
|
+
copy_related_component_files(component_name)
|
|
20
|
+
copy_js_files(component_name)
|
|
21
|
+
install_dependencies(component_name)
|
|
18
22
|
end
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
update_stimulus_manifest
|
|
21
25
|
end
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def validate_components!
|
|
30
|
+
missing = component_names.reject { |name| component_exists?(name) }
|
|
31
|
+
return if missing.empty?
|
|
32
|
+
|
|
33
|
+
say "Component(s) not found: #{missing.join(", ")}", :red
|
|
34
|
+
exit 1
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def copy_related_component_files(component_name)
|
|
24
38
|
say "Generating components"
|
|
25
39
|
|
|
26
|
-
components_file_paths.each do |file_path|
|
|
40
|
+
components_file_paths(component_name).each do |file_path|
|
|
27
41
|
component_file_name = file_path.split("/").last
|
|
28
|
-
copy_file file_path, Rails.root.join("app/components/ruby_ui", component_folder_name, component_file_name), force: options["force"]
|
|
42
|
+
copy_file file_path, Rails.root.join("app/components/ruby_ui", component_folder_name(component_name), component_file_name), force: options["force"]
|
|
29
43
|
end
|
|
30
44
|
end
|
|
31
45
|
|
|
32
|
-
def copy_js_files
|
|
33
|
-
|
|
46
|
+
def copy_js_files(component_name)
|
|
47
|
+
paths = js_controller_file_paths(component_name)
|
|
48
|
+
return if paths.empty?
|
|
34
49
|
|
|
35
50
|
say "Generating Stimulus controllers"
|
|
36
51
|
|
|
37
|
-
|
|
52
|
+
paths.each do |file_path|
|
|
38
53
|
controller_file_name = file_path.split("/").last
|
|
39
54
|
copy_file file_path, Rails.root.join("app/javascript/controllers/ruby_ui", controller_file_name), force: options["force"]
|
|
40
55
|
end
|
|
41
56
|
|
|
57
|
+
@stimulus_controllers_added = true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def update_stimulus_manifest
|
|
61
|
+
return unless @stimulus_controllers_added
|
|
62
|
+
|
|
42
63
|
# Importmap doesn't have controller manifest, instead it uses `eagerLoadControllersFrom("controllers", application)`
|
|
43
|
-
if
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
return if using_importmap?
|
|
65
|
+
|
|
66
|
+
say "Updating Stimulus controllers manifest"
|
|
67
|
+
run "rake stimulus:manifest:update"
|
|
47
68
|
end
|
|
48
69
|
|
|
49
|
-
def install_dependencies
|
|
50
|
-
|
|
70
|
+
def install_dependencies(component_name)
|
|
71
|
+
deps = dependencies(component_name)
|
|
72
|
+
return if deps.blank?
|
|
51
73
|
|
|
52
74
|
say "Installing dependencies"
|
|
53
75
|
|
|
54
|
-
install_components_dependencies(
|
|
55
|
-
install_gems_dependencies(
|
|
56
|
-
install_js_packages(
|
|
76
|
+
install_components_dependencies(deps["components"])
|
|
77
|
+
install_gems_dependencies(deps["gems"])
|
|
78
|
+
install_js_packages(deps["js_packages"])
|
|
57
79
|
end
|
|
58
80
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def component_not_found? = !Dir.exist?(component_folder_path)
|
|
81
|
+
def component_exists?(component_name) = Dir.exist?(component_folder_path(component_name))
|
|
62
82
|
|
|
63
|
-
def component_folder_name = component_name.underscore
|
|
83
|
+
def component_folder_name(component_name) = component_name.underscore
|
|
64
84
|
|
|
65
|
-
def component_folder_path = File.join(self.class.source_root, component_folder_name)
|
|
85
|
+
def component_folder_path(component_name) = File.join(self.class.source_root, component_folder_name(component_name))
|
|
66
86
|
|
|
67
|
-
def components_file_paths
|
|
68
|
-
files = Dir.glob(File.join(component_folder_path, "*.rb"))
|
|
87
|
+
def components_file_paths(component_name)
|
|
88
|
+
files = Dir.glob(File.join(component_folder_path(component_name), "*.rb"))
|
|
69
89
|
options["with_docs"] ? files : files.reject { |f| f.end_with?("_docs.rb") }
|
|
70
90
|
end
|
|
71
91
|
|
|
72
|
-
def js_controller_file_paths = Dir.glob(File.join(component_folder_path, "*.js"))
|
|
92
|
+
def js_controller_file_paths(component_name) = Dir.glob(File.join(component_folder_path(component_name), "*.js"))
|
|
73
93
|
|
|
74
94
|
def install_components_dependencies(components)
|
|
75
95
|
components&.each do |component|
|
|
@@ -89,10 +109,10 @@ module RubyUI
|
|
|
89
109
|
end
|
|
90
110
|
end
|
|
91
111
|
|
|
92
|
-
def dependencies
|
|
112
|
+
def dependencies(component_name)
|
|
93
113
|
@dependencies ||= YAML.load_file(File.join(__dir__, "dependencies.yml")).freeze
|
|
94
114
|
|
|
95
|
-
@dependencies[component_folder_name]
|
|
115
|
+
@dependencies[component_folder_name(component_name)]
|
|
96
116
|
end
|
|
97
117
|
end
|
|
98
118
|
end
|
|
@@ -11,10 +11,12 @@ module RubyUI
|
|
|
11
11
|
def default_attrs
|
|
12
12
|
{
|
|
13
13
|
data: {
|
|
14
|
-
ruby_ui__accordion_target: "content"
|
|
14
|
+
ruby_ui__accordion_target: "content",
|
|
15
|
+
state: "closed"
|
|
15
16
|
},
|
|
16
17
|
class: "overflow-y-hidden",
|
|
17
|
-
style: "height: 0px;"
|
|
18
|
+
style: "height: 0px;",
|
|
19
|
+
hidden: true
|
|
18
20
|
}
|
|
19
21
|
end
|
|
20
22
|
end
|
|
@@ -65,9 +65,15 @@ export default class extends Controller {
|
|
|
65
65
|
|
|
66
66
|
// Reveal the accordion content with animation
|
|
67
67
|
revealContent() {
|
|
68
|
-
const
|
|
68
|
+
const content = this.contentTarget;
|
|
69
|
+
|
|
70
|
+
// Remove hidden so the element participates in layout before measuring
|
|
71
|
+
content.removeAttribute("hidden");
|
|
72
|
+
content.dataset.state = "open";
|
|
73
|
+
|
|
74
|
+
const contentHeight = content.scrollHeight;
|
|
69
75
|
animate(
|
|
70
|
-
|
|
76
|
+
content,
|
|
71
77
|
{ height: `${contentHeight}px` },
|
|
72
78
|
{
|
|
73
79
|
duration: this.animationDurationValue,
|
|
@@ -78,14 +84,23 @@ export default class extends Controller {
|
|
|
78
84
|
|
|
79
85
|
// Hide the accordion content with animation
|
|
80
86
|
hideContent() {
|
|
87
|
+
const content = this.contentTarget;
|
|
88
|
+
content.dataset.state = "closed";
|
|
89
|
+
|
|
81
90
|
animate(
|
|
82
|
-
|
|
91
|
+
content,
|
|
83
92
|
{ height: 0 },
|
|
84
93
|
{
|
|
85
94
|
duration: this.animationDurationValue,
|
|
86
95
|
easing: this.animationEasingValue,
|
|
87
96
|
},
|
|
88
|
-
)
|
|
97
|
+
).finished.then(() => {
|
|
98
|
+
// After animation completes, truly hide the element so it is removed
|
|
99
|
+
// from layout and form focus — prevents trapped validation errors
|
|
100
|
+
if (content.dataset.state === "closed") {
|
|
101
|
+
content.setAttribute("hidden", "");
|
|
102
|
+
}
|
|
103
|
+
});
|
|
89
104
|
}
|
|
90
105
|
|
|
91
106
|
// Rotate the accordion icon 180deg using animate function
|
|
@@ -16,7 +16,11 @@ module RubyUI
|
|
|
16
16
|
|
|
17
17
|
def default_attrs
|
|
18
18
|
{
|
|
19
|
-
loading: "lazy"
|
|
19
|
+
# NB: do not set loading: "lazy" here. avatar_controller hides a not-yet-loaded
|
|
20
|
+
# image with `display:none` (the `hidden` class) so the fallback shows. The
|
|
21
|
+
# browser never fetches a `loading="lazy"` image that generates no box, so its
|
|
22
|
+
# `load` event never fires and the image stays hidden forever (#415). shadcn/radix
|
|
23
|
+
# do not lazy-load the avatar image either.
|
|
20
24
|
data: {
|
|
21
25
|
ruby_ui__avatar_target: "image",
|
|
22
26
|
action: "load->ruby-ui--avatar#showImage error->ruby-ui--avatar#showFallback"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Views::Docs::DataTable < Views::Base
|
|
4
|
-
Row = Struct.new(:id, :name, :email, :salary, :status
|
|
4
|
+
Row = Struct.new(:id, :name, :email, :salary, :status)
|
|
5
5
|
|
|
6
6
|
SAMPLE_ROWS = [
|
|
7
7
|
Row.new(id: 1, name: "Alice", email: "alice@example.com", salary: 90_000, status: "Active"),
|
|
@@ -17,14 +17,9 @@ module RubyUI
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def view_template
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
div(**attrs) do
|
|
24
|
-
yield
|
|
25
|
-
close_button
|
|
26
|
-
end
|
|
27
|
-
end
|
|
20
|
+
dialog(**attrs) do
|
|
21
|
+
yield
|
|
22
|
+
close_button
|
|
28
23
|
end
|
|
29
24
|
end
|
|
30
25
|
|
|
@@ -32,9 +27,10 @@ module RubyUI
|
|
|
32
27
|
|
|
33
28
|
def default_attrs
|
|
34
29
|
{
|
|
35
|
-
|
|
30
|
+
data_ruby_ui__dialog_target: "dialog",
|
|
31
|
+
data_action: "click->ruby-ui--dialog#backdropClick",
|
|
36
32
|
class: [
|
|
37
|
-
"fixed flex flex-col pointer-events-auto left-[50%] top-[50%] z-50 w-full max-h-screen overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200
|
|
33
|
+
"fixed flex flex-col pointer-events-auto left-[50%] top-[50%] z-50 w-full max-h-screen overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 backdrop:bg-background/80 backdrop:backdrop-blur-sm open:animate-in open:fade-in-0 open:zoom-in-95 sm:rounded-lg md:w-full",
|
|
38
34
|
SIZES[@size]
|
|
39
35
|
]
|
|
40
36
|
}
|
|
@@ -43,7 +39,7 @@ module RubyUI
|
|
|
43
39
|
def close_button
|
|
44
40
|
button(
|
|
45
41
|
type: "button",
|
|
46
|
-
class: "absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none
|
|
42
|
+
class: "absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none",
|
|
47
43
|
data_action: "click->ruby-ui--dialog#dismiss"
|
|
48
44
|
) do
|
|
49
45
|
svg(
|
|
@@ -65,13 +61,5 @@ module RubyUI
|
|
|
65
61
|
span(class: "sr-only") { "Close" }
|
|
66
62
|
end
|
|
67
63
|
end
|
|
68
|
-
|
|
69
|
-
def backdrop
|
|
70
|
-
div(
|
|
71
|
-
data_state: "open",
|
|
72
|
-
data_action: "click->ruby-ui--dialog#dismiss esc->ruby-ui--dialog#dismiss",
|
|
73
|
-
class: "fixed pointer-events-auto inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=open]:fade-in-0"
|
|
74
|
-
)
|
|
75
|
-
end
|
|
76
64
|
end
|
|
77
65
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Controller } from "@hotwired/stimulus"
|
|
2
2
|
|
|
3
|
-
// Connects to data-controller="dialog"
|
|
3
|
+
// Connects to data-controller="ruby-ui--dialog"
|
|
4
4
|
export default class extends Controller {
|
|
5
|
-
static targets = ["
|
|
5
|
+
static targets = ["dialog"]
|
|
6
6
|
static values = {
|
|
7
7
|
open: {
|
|
8
8
|
type: Boolean,
|
|
@@ -11,22 +11,34 @@ export default class extends Controller {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
connect() {
|
|
14
|
+
this.dialogTarget.addEventListener("close", this.handleClose)
|
|
14
15
|
if (this.openValue) {
|
|
15
16
|
this.open()
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
disconnect() {
|
|
21
|
+
this.dialogTarget.removeEventListener("close", this.handleClose)
|
|
22
|
+
document.body.classList.remove("overflow-hidden")
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
open(e) {
|
|
20
|
-
e?.preventDefault()
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
document.body.classList.add('overflow-hidden')
|
|
26
|
+
e?.preventDefault()
|
|
27
|
+
this.dialogTarget.showModal()
|
|
28
|
+
document.body.classList.add("overflow-hidden")
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
dismiss() {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
this.dialogTarget.close()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
backdropClick(e) {
|
|
36
|
+
if (e.target === this.dialogTarget) {
|
|
37
|
+
this.dismiss()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
handleClose = () => {
|
|
42
|
+
document.body.classList.remove("overflow-hidden")
|
|
31
43
|
}
|
|
32
44
|
}
|
|
@@ -2,20 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyUI
|
|
4
4
|
class DropdownMenuItem < Base
|
|
5
|
-
def initialize(href: "#", **attrs)
|
|
5
|
+
def initialize(as: :a, href: "#", **attrs)
|
|
6
|
+
@as = as
|
|
6
7
|
@href = href
|
|
7
8
|
super(**attrs)
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def view_template(&)
|
|
11
|
-
|
|
12
|
+
if @as == :div
|
|
13
|
+
div(**attrs, &)
|
|
14
|
+
else
|
|
15
|
+
a(**attrs, &)
|
|
16
|
+
end
|
|
12
17
|
end
|
|
13
18
|
|
|
14
19
|
private
|
|
15
20
|
|
|
16
21
|
def default_attrs
|
|
17
|
-
{
|
|
18
|
-
href: @href,
|
|
22
|
+
base = {
|
|
19
23
|
role: "menuitem",
|
|
20
24
|
class: "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",
|
|
21
25
|
data_action: "click->ruby-ui--dropdown-menu#close",
|
|
@@ -23,6 +27,8 @@ module RubyUI
|
|
|
23
27
|
tabindex: "-1",
|
|
24
28
|
data_orientation: "vertical"
|
|
25
29
|
}
|
|
30
|
+
base[:href] = @href unless @as == :div
|
|
31
|
+
base
|
|
26
32
|
end
|
|
27
33
|
end
|
|
28
34
|
end
|
|
@@ -170,6 +170,95 @@ class Views::Docs::Form < Views::Base
|
|
|
170
170
|
RUBY
|
|
171
171
|
end
|
|
172
172
|
|
|
173
|
+
Heading(level: 2) { "Rails Integration" }
|
|
174
|
+
|
|
175
|
+
Text do
|
|
176
|
+
plain "RubyUI Form components are plain HTML — they work with any form submission strategy. "
|
|
177
|
+
plain "The recommended approach for Rails apps is to use "
|
|
178
|
+
InlineLink(href: "https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with", target: "_blank") { "form_with" }
|
|
179
|
+
plain " to generate the "
|
|
180
|
+
code(class: "font-mono text-sm") { "action" }
|
|
181
|
+
plain " URL and CSRF token, then pass explicit "
|
|
182
|
+
code(class: "font-mono text-sm") { "name" }
|
|
183
|
+
plain " / "
|
|
184
|
+
code(class: "font-mono text-sm") { "id" }
|
|
185
|
+
plain " attributes to each RubyUI input so the browser serialises them correctly. "
|
|
186
|
+
plain "Server-side errors can be surfaced by rendering "
|
|
187
|
+
code(class: "font-mono text-sm") { "FormFieldError" }
|
|
188
|
+
plain " with content from "
|
|
189
|
+
code(class: "font-mono text-sm") { "model.errors.full_messages_for(:attr)" }
|
|
190
|
+
plain "."
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
Heading(level: 3) { "Minimal Rails form" }
|
|
194
|
+
Codeblock(<<~RUBY, syntax: :ruby)
|
|
195
|
+
# In your Phlex view, call form_with via helpers:
|
|
196
|
+
# form_with(url: users_path, method: :post) passes action + CSRF automatically.
|
|
197
|
+
#
|
|
198
|
+
# You can also set action and the CSRF token manually:
|
|
199
|
+
Form(action: helpers.users_path, method: "post", class: "w-2/3 space-y-6") do
|
|
200
|
+
input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token)
|
|
201
|
+
|
|
202
|
+
FormField do
|
|
203
|
+
FormFieldLabel(for: "user_email") { "Email" }
|
|
204
|
+
Input(
|
|
205
|
+
type: "email",
|
|
206
|
+
id: "user_email",
|
|
207
|
+
name: "user[email]",
|
|
208
|
+
placeholder: "you@example.com",
|
|
209
|
+
required: true
|
|
210
|
+
)
|
|
211
|
+
FormFieldError()
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
Button(type: "submit") { "Continue" }
|
|
215
|
+
end
|
|
216
|
+
RUBY
|
|
217
|
+
|
|
218
|
+
Heading(level: 3) { "Devise-style login form" }
|
|
219
|
+
Codeblock(<<~RUBY, syntax: :ruby)
|
|
220
|
+
# Full sign-in form mirroring Devise session[email] / session[password] params.
|
|
221
|
+
# Pass backend errors (e.g. "Invalid email or password") into FormFieldError.
|
|
222
|
+
Form(action: helpers.user_session_path, method: "post", class: "space-y-6") do
|
|
223
|
+
input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token)
|
|
224
|
+
|
|
225
|
+
FormField do
|
|
226
|
+
FormFieldLabel(for: "session_email") { "Email" }
|
|
227
|
+
Input(
|
|
228
|
+
type: "email",
|
|
229
|
+
id: "session_email",
|
|
230
|
+
name: "session[email]",
|
|
231
|
+
placeholder: "you@example.com",
|
|
232
|
+
autocomplete: "email",
|
|
233
|
+
required: true
|
|
234
|
+
)
|
|
235
|
+
FormFieldError { @error_message }
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
FormField do
|
|
239
|
+
FormFieldLabel(for: "session_password") { "Password" }
|
|
240
|
+
Input(
|
|
241
|
+
type: "password",
|
|
242
|
+
id: "session_password",
|
|
243
|
+
name: "session[password]",
|
|
244
|
+
autocomplete: "current-password",
|
|
245
|
+
required: true,
|
|
246
|
+
minlength: "8"
|
|
247
|
+
)
|
|
248
|
+
FormFieldError()
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
FormField do
|
|
252
|
+
div(class: "flex items-center gap-2") do
|
|
253
|
+
Checkbox(id: "session_remember_me", name: "session[remember_me]", value: "1")
|
|
254
|
+
FormFieldLabel(for: "session_remember_me") { "Remember me" }
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
Button(type: "submit", class: "w-full") { "Sign in" }
|
|
259
|
+
end
|
|
260
|
+
RUBY
|
|
261
|
+
|
|
173
262
|
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
174
263
|
|
|
175
264
|
render Docs::ComponentsTable.new(component_files(component))
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Views::Docs::Table < Views::Base
|
|
4
|
-
Invoice = Struct.new(:identifier, :status, :method, :amount
|
|
5
|
-
User = Struct.new(:avatar_url, :name, :username, :commits, :github_url
|
|
4
|
+
Invoice = Struct.new(:identifier, :status, :method, :amount)
|
|
5
|
+
User = Struct.new(:avatar_url, :name, :username, :commits, :github_url)
|
|
6
6
|
|
|
7
7
|
def view_template
|
|
8
8
|
component = "Table"
|
|
@@ -2,20 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyUI
|
|
4
4
|
class TabsTrigger < Base
|
|
5
|
-
def initialize(value:, **attrs)
|
|
5
|
+
def initialize(value:, as: :button, **attrs)
|
|
6
6
|
@value = value
|
|
7
|
+
@as = as
|
|
7
8
|
super(**attrs)
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def view_template(&)
|
|
11
|
-
|
|
12
|
+
if @as == :a
|
|
13
|
+
a(**attrs, &)
|
|
14
|
+
else
|
|
15
|
+
button(**attrs, &)
|
|
16
|
+
end
|
|
12
17
|
end
|
|
13
18
|
|
|
14
19
|
private
|
|
15
20
|
|
|
16
21
|
def default_attrs
|
|
17
|
-
{
|
|
18
|
-
type: :button,
|
|
22
|
+
base = {
|
|
19
23
|
data: {
|
|
20
24
|
ruby_ui__tabs_target: "trigger",
|
|
21
25
|
action: "click->ruby-ui--tabs#show",
|
|
@@ -29,6 +33,8 @@ module RubyUI
|
|
|
29
33
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
|
30
34
|
]
|
|
31
35
|
}
|
|
36
|
+
base[:type] = :button unless @as == :a
|
|
37
|
+
base
|
|
32
38
|
end
|
|
33
39
|
end
|
|
34
40
|
end
|
data/lib/ruby_ui.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- George Kettle
|
|
@@ -453,7 +453,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
453
453
|
- !ruby/object:Gem::Version
|
|
454
454
|
version: '0'
|
|
455
455
|
requirements: []
|
|
456
|
-
rubygems_version:
|
|
456
|
+
rubygems_version: 3.6.9
|
|
457
457
|
specification_version: 4
|
|
458
458
|
summary: RubyUI is a UI Component Library for Ruby developers.
|
|
459
459
|
test_files: []
|