rails-active-ui 0.2.0 → 0.2.1
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/app/components/accordion_component.rb +26 -17
- data/app/components/button_to_component.rb +43 -0
- data/app/components/form_component.rb +50 -33
- data/app/components/icon_component.rb +1 -1
- data/app/components/input_component.rb +22 -0
- data/app/components/link_component.rb +1 -1
- data/app/components/modal_component.rb +14 -3
- data/app/components/segment_component.rb +1 -1
- data/app/components/style_component.rb +23 -0
- data/app/components/wrapper_component.rb +17 -0
- data/app/helpers/component_helper.rb +31 -0
- data/app/javascript/ui/controllers/fui_emoji_picker_controller.js +61 -0
- data/app/javascript/ui/index.js +3 -0
- data/app/lib/component.rb +26 -0
- data/config/importmap.rb +3 -0
- data/lib/rails_active_ui.rb +8 -0
- data/lib/ui/version.rb +1 -1
- data/lib/ui.rb +0 -4
- metadata +11 -4
- data/lib/rails-active-ui.rb +0 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b4649f0a2a53b0e99341193bdb0e2def4d8aa2aa9280d122022314062472c932
|
|
4
|
+
data.tar.gz: 318b3c477e40e511d75b97693a08710e61d9ab52b5e9a3b2221023e2e5a55b2c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8aec1d05c6616b0db44bef667f1fa7db57d47d85e8b8739a26af09c8cf5adf5288745181d8e7ecc2304058a52f716a2f36f8217ed1f7784368c3ab112cbf510c
|
|
7
|
+
data.tar.gz: ca4ede8a537dad55924a4e4eec6487c7207b8b422322842c79d851bd437396c6ceabc1520c87f516df78ac7e8b35539c5d9f99c978523143d75741f3440a1042
|
|
@@ -1,34 +1,43 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Accordion — collapsible content
|
|
3
|
+
# Accordion — collapsible content panel using <details>/<summary>.
|
|
4
4
|
#
|
|
5
5
|
# Usage:
|
|
6
|
-
# Accordion(
|
|
7
|
-
#
|
|
8
|
-
# Icon(name: "dropdown")
|
|
9
|
-
# text " Section"
|
|
10
|
-
# }
|
|
6
|
+
# Accordion(attached: true) { |a|
|
|
7
|
+
# a.title { text "Section" }
|
|
11
8
|
# text "Panel content"
|
|
12
9
|
# }
|
|
13
10
|
|
|
14
11
|
class AccordionComponent < Component
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
attribute :
|
|
18
|
-
attribute :
|
|
19
|
-
attribute :
|
|
12
|
+
include Attachable
|
|
13
|
+
|
|
14
|
+
attribute :raised, :boolean, default: false
|
|
15
|
+
attribute :inverted, :boolean, default: false
|
|
16
|
+
attribute :basic, :boolean, default: false
|
|
17
|
+
attribute :compact, :boolean, default: false
|
|
18
|
+
attribute :color, :string, default: nil
|
|
19
|
+
attribute :secondary, :boolean, default: false
|
|
20
|
+
attribute :open, :boolean, default: false
|
|
21
|
+
|
|
22
|
+
slot :title
|
|
20
23
|
|
|
21
24
|
def to_s
|
|
22
25
|
classes = class_names(
|
|
23
26
|
"ui",
|
|
24
|
-
|
|
25
|
-
"
|
|
27
|
+
color,
|
|
28
|
+
{ "attached" => attached, "raised" => raised, "inverted" => inverted,
|
|
29
|
+
"basic" => basic, "compact" => compact, "secondary" => secondary },
|
|
30
|
+
"segment"
|
|
26
31
|
)
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
data[:fui_accordion_collapsible_value] = "false" unless collapsible
|
|
33
|
+
opts = merge_html_options(class: classes)
|
|
34
|
+
opts[:open] = "" if open
|
|
31
35
|
|
|
32
|
-
tag.
|
|
36
|
+
tag.details(**opts) {
|
|
37
|
+
safe_join([
|
|
38
|
+
tag.summary { @slots[:title] || "" },
|
|
39
|
+
tag.div { @content }
|
|
40
|
+
])
|
|
41
|
+
}
|
|
33
42
|
end
|
|
34
43
|
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# ButtonTo — a form-based button for non-GET actions (DELETE, PATCH, etc).
|
|
4
|
+
#
|
|
5
|
+
# Wraps Rails button_to with Fomantic-UI button styling.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# ButtonTo(url: "/items/1", method: :delete, color: "red", confirm: "Sure?") {
|
|
9
|
+
# Icon(name: "trash")
|
|
10
|
+
# text " Delete"
|
|
11
|
+
# }
|
|
12
|
+
|
|
13
|
+
class ButtonToComponent < Component
|
|
14
|
+
include Sizeable
|
|
15
|
+
|
|
16
|
+
attribute :url, :string
|
|
17
|
+
attribute :method, :string, default: "post"
|
|
18
|
+
attribute :color, :string, default: nil
|
|
19
|
+
attribute :variant, :string, default: nil
|
|
20
|
+
attribute :icon, :string, default: nil
|
|
21
|
+
attribute :disabled, :boolean, default: false
|
|
22
|
+
attribute :confirm, :string, default: nil
|
|
23
|
+
|
|
24
|
+
def to_s
|
|
25
|
+
classes = class_names(
|
|
26
|
+
"ui",
|
|
27
|
+
color,
|
|
28
|
+
size,
|
|
29
|
+
variant,
|
|
30
|
+
{ "disabled" => disabled },
|
|
31
|
+
"button"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
form_opts = { method: method.to_sym }
|
|
35
|
+
form_opts[:data] = { turbo_confirm: confirm } if confirm
|
|
36
|
+
|
|
37
|
+
@view_context.button_to(
|
|
38
|
+
url,
|
|
39
|
+
form: form_opts,
|
|
40
|
+
class: classes
|
|
41
|
+
) { @content }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -1,39 +1,56 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
3
|
+
class FormComponent
|
|
4
|
+
def initialize(**kwargs)
|
|
5
|
+
@form_options = kwargs
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def render_in(context, &block)
|
|
9
|
+
@view_context = context
|
|
10
|
+
|
|
11
|
+
if @form_options[:url] || @form_options[:model]
|
|
12
|
+
@_block = block
|
|
13
|
+
if Rails.env.development?
|
|
14
|
+
"<!-- BEGIN #{self.class.name} -->#{self}<!-- END #{self.class.name} -->".html_safe
|
|
15
|
+
else
|
|
16
|
+
to_s
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
@content = context.capture(self, &block) if block
|
|
20
|
+
@content = ERB::Util.html_escape(@content) unless @content.nil? || @content.html_safe?
|
|
21
|
+
if Rails.env.development?
|
|
22
|
+
"<!-- BEGIN #{self.class.name} -->#{self}<!-- END #{self.class.name} -->".html_safe
|
|
23
|
+
else
|
|
24
|
+
to_s
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
22
28
|
|
|
23
29
|
def to_s
|
|
24
|
-
|
|
25
|
-
"ui",
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
if @form_options[:url] || @form_options[:model]
|
|
31
|
+
opts = { class: "ui form", data: { controller: "fui-form" } }
|
|
32
|
+
opts.merge!(@form_options)
|
|
33
|
+
|
|
34
|
+
@view_context.form_with(**opts) { |f|
|
|
35
|
+
previous = @view_context.instance_variable_get(:@_form_builder)
|
|
36
|
+
@view_context.instance_variable_set(:@_form_builder, f)
|
|
37
|
+
begin
|
|
38
|
+
@_block ? @view_context.instance_exec(&@_block) : "".html_safe
|
|
39
|
+
ensure
|
|
40
|
+
@view_context.instance_variable_set(:@_form_builder, previous)
|
|
41
|
+
end
|
|
42
|
+
}
|
|
43
|
+
else
|
|
44
|
+
opts = { class: "ui form", data: { controller: "fui-form" } }
|
|
45
|
+
opts.merge!(@form_options)
|
|
46
|
+
|
|
47
|
+
tag.form(**opts) { @content }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def tag
|
|
54
|
+
@view_context.tag
|
|
38
55
|
end
|
|
39
56
|
end
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
# Input(placeholder: "Search...", icon: "search")
|
|
7
7
|
# Input(icon: "users", icon_position: "left", placeholder: "Find users...")
|
|
8
8
|
# Input(labeled: true) { text "$" }
|
|
9
|
+
# Input(name: "email", label: "Email", placeholder: "you@example.com")
|
|
10
|
+
# Input(name: "password", label: "Password", input_type: "password")
|
|
9
11
|
|
|
10
12
|
class InputComponent < Component
|
|
11
13
|
attribute :icon, :string, default: nil
|
|
@@ -23,8 +25,27 @@ class InputComponent < Component
|
|
|
23
25
|
attribute :input_type, :string, default: "text"
|
|
24
26
|
attribute :name, :string, default: nil
|
|
25
27
|
attribute :value, :string, default: nil
|
|
28
|
+
attribute :label, :string, default: nil
|
|
29
|
+
attribute :id, :string, default: nil
|
|
26
30
|
|
|
27
31
|
def to_s
|
|
32
|
+
input_field = input_element
|
|
33
|
+
|
|
34
|
+
if label
|
|
35
|
+
tag.div(class: "field") {
|
|
36
|
+
safe_join([
|
|
37
|
+
tag.label(label, for: id || name),
|
|
38
|
+
input_field
|
|
39
|
+
])
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
input_field
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def input_element
|
|
28
49
|
classes = class_names(
|
|
29
50
|
"ui",
|
|
30
51
|
size,
|
|
@@ -41,6 +62,7 @@ class InputComponent < Component
|
|
|
41
62
|
input_opts[:name] = name if name
|
|
42
63
|
input_opts[:value] = value if value
|
|
43
64
|
input_opts[:disabled] = "disabled" if disabled
|
|
65
|
+
input_opts[:id] = id if id
|
|
44
66
|
|
|
45
67
|
icon_el = icon ? tag.i(class: "#{icon} icon") : nil
|
|
46
68
|
input_el = tag.input(**input_opts)
|
|
@@ -11,14 +11,21 @@
|
|
|
11
11
|
# Button(variant: :approve, color: :green) { text "OK" }
|
|
12
12
|
# }
|
|
13
13
|
# }
|
|
14
|
+
#
|
|
15
|
+
# Modal(id: "confirm-modal", size: :large) { |c|
|
|
16
|
+
# c.header { text "Targeted Modal" }
|
|
17
|
+
# c.content { text "This modal can be targeted by DOM id." }
|
|
18
|
+
# }
|
|
14
19
|
|
|
15
20
|
class ModalComponent < Component
|
|
21
|
+
attribute :id, :string, default: nil
|
|
16
22
|
attribute :size, :string, default: nil
|
|
17
23
|
attribute :basic, :boolean, default: false
|
|
18
24
|
attribute :closable, :boolean, default: true
|
|
19
25
|
attribute :blurring, :boolean, default: false
|
|
20
|
-
attribute :longer,
|
|
26
|
+
attribute :longer, :boolean, default: false
|
|
21
27
|
attribute :fullscreen, :boolean, default: false
|
|
28
|
+
attribute :scrolling, :boolean, default: false
|
|
22
29
|
|
|
23
30
|
slot :header
|
|
24
31
|
slot :content
|
|
@@ -40,10 +47,14 @@ class ModalComponent < Component
|
|
|
40
47
|
|
|
41
48
|
close_el = closable ? tag.i(class: "close icon") : nil
|
|
42
49
|
header_el = @slots[:header] ? tag.div(class: "header") { @slots[:header] } : nil
|
|
43
|
-
|
|
50
|
+
content_cls = scrolling ? "scrolling content" : "content"
|
|
51
|
+
content_el = @slots[:content] ? tag.div(class: content_cls) { @slots[:content] } : nil
|
|
44
52
|
actions_el = @slots[:actions] ? tag.div(class: "actions") { @slots[:actions] } : nil
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
opts = { class: classes, data: data }
|
|
55
|
+
opts[:id] = id if id
|
|
56
|
+
|
|
57
|
+
tag.div(**opts) {
|
|
47
58
|
safe_join([ close_el, header_el, content_el, @content.presence, actions_el ])
|
|
48
59
|
}
|
|
49
60
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Style — inline <style> tag.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Style(".my-class { color: red; }")
|
|
7
|
+
|
|
8
|
+
class StyleComponent < Component
|
|
9
|
+
def initialize(css = nil, **kwargs)
|
|
10
|
+
@css = css
|
|
11
|
+
super(**kwargs)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render_in(context, &block)
|
|
15
|
+
@view_context = context
|
|
16
|
+
@css ||= context.capture(&block) if block
|
|
17
|
+
to_s
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_s
|
|
21
|
+
tag.style { @css&.html_safe }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Wrapper — a plain <div> with optional id/class.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Wrapper(id: "main") { text "content" }
|
|
7
|
+
# Wrapper(html_class: "scrollable") { text "content" }
|
|
8
|
+
|
|
9
|
+
class WrapperComponent < Component
|
|
10
|
+
attribute :html_class, :string, default: nil
|
|
11
|
+
|
|
12
|
+
def to_s
|
|
13
|
+
opts = {}
|
|
14
|
+
opts[:class] = html_class if html_class
|
|
15
|
+
tag.div(**merge_html_options(**opts)) { @content }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -25,9 +25,11 @@ module ComponentHelper
|
|
|
25
25
|
# Globals
|
|
26
26
|
Reset: ResetComponent,
|
|
27
27
|
Site: SiteComponent,
|
|
28
|
+
Wrapper: WrapperComponent,
|
|
28
29
|
|
|
29
30
|
# Elements
|
|
30
31
|
Button: ButtonComponent,
|
|
32
|
+
ButtonTo: ButtonToComponent,
|
|
31
33
|
Container: ContainerComponent,
|
|
32
34
|
Divider: DividerComponent,
|
|
33
35
|
Emoji: EmojiComponent,
|
|
@@ -46,6 +48,7 @@ module ComponentHelper
|
|
|
46
48
|
SegmentGroup: SegmentGroupComponent,
|
|
47
49
|
Step: StepComponent,
|
|
48
50
|
StepGroup: StepGroupComponent,
|
|
51
|
+
|
|
49
52
|
Text: TextComponent,
|
|
50
53
|
|
|
51
54
|
# Collections
|
|
@@ -103,7 +106,35 @@ module ComponentHelper
|
|
|
103
106
|
}
|
|
104
107
|
end
|
|
105
108
|
|
|
109
|
+
def Style(css = nil, &block)
|
|
110
|
+
output_buffer << render(StyleComponent.new(css), &block)
|
|
111
|
+
end
|
|
112
|
+
|
|
106
113
|
def text(content)
|
|
107
114
|
output_buffer << content.to_s
|
|
108
115
|
end
|
|
116
|
+
|
|
117
|
+
# PascalCase method calls that aren't in COMPONENT_MAP are forwarded
|
|
118
|
+
# to the current form builder as underscored method names.
|
|
119
|
+
# e.g. TextField(:label, placeholder: "Name") -> f.text_field(:label, placeholder: "Name")
|
|
120
|
+
# EmojiField(:icon) -> f.emoji_field(:icon)
|
|
121
|
+
# Select(:role, [["Admin","admin"]]) -> f.select(:role, [["Admin","admin"]])
|
|
122
|
+
def method_missing(method_name, *args, **kwargs, &block)
|
|
123
|
+
if method_name =~ /\A[A-Z]/ && @_form_builder
|
|
124
|
+
underscored = method_name.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
|
125
|
+
if @_form_builder.respond_to?(underscored)
|
|
126
|
+
output_buffer << @_form_builder.public_send(underscored, *args, **kwargs, &block)
|
|
127
|
+
return
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
super
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
134
|
+
if method_name =~ /\A[A-Z]/ && @_form_builder
|
|
135
|
+
underscored = method_name.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
|
136
|
+
return true if @_form_builder.respond_to?(underscored)
|
|
137
|
+
end
|
|
138
|
+
super
|
|
139
|
+
end
|
|
109
140
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import "emoji-picker-element"
|
|
3
|
+
|
|
4
|
+
// Fomantic-UI Emoji Picker — click-to-pick emoji input.
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// <div data-controller="fui-emoji-picker">
|
|
8
|
+
// <input data-fui-emoji-picker-target="input" type="hidden" name="icon" />
|
|
9
|
+
// <button data-fui-emoji-picker-target="preview" data-action="click->fui-emoji-picker#toggle" type="button">
|
|
10
|
+
// Pick emoji
|
|
11
|
+
// </button>
|
|
12
|
+
// <div data-fui-emoji-picker-target="dropdown" style="display:none; position:absolute; z-index:1000;">
|
|
13
|
+
// <!-- emoji-picker is appended here -->
|
|
14
|
+
// </div>
|
|
15
|
+
// </div>
|
|
16
|
+
|
|
17
|
+
export default class extends Controller {
|
|
18
|
+
static targets = ["input", "preview", "dropdown"]
|
|
19
|
+
|
|
20
|
+
connect() {
|
|
21
|
+
this.picker = document.createElement("emoji-picker")
|
|
22
|
+
this.dropdownTarget.appendChild(this.picker)
|
|
23
|
+
|
|
24
|
+
this.picker.addEventListener("emoji-click", (e) => {
|
|
25
|
+
this.inputTarget.value = e.detail.unicode
|
|
26
|
+
this.previewTarget.textContent = e.detail.unicode
|
|
27
|
+
this.close()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
this._outsideClick = (e) => {
|
|
31
|
+
if (this.isOpen && !this.element.contains(e.target)) {
|
|
32
|
+
this.close()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
document.addEventListener("click", this._outsideClick)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
disconnect() {
|
|
39
|
+
document.removeEventListener("click", this._outsideClick)
|
|
40
|
+
if (this.picker) {
|
|
41
|
+
this.picker.remove()
|
|
42
|
+
this.picker = null
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
toggle(e) {
|
|
47
|
+
e.preventDefault()
|
|
48
|
+
e.stopPropagation()
|
|
49
|
+
this.isOpen ? this.close() : this.open()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
open() {
|
|
53
|
+
this.dropdownTarget.style.display = "block"
|
|
54
|
+
this.isOpen = true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
close() {
|
|
58
|
+
this.dropdownTarget.style.display = "none"
|
|
59
|
+
this.isOpen = false
|
|
60
|
+
}
|
|
61
|
+
}
|
data/app/javascript/ui/index.js
CHANGED
|
@@ -49,6 +49,7 @@ import FuiTransitionController from "ui/controllers/fui_transition_controller"
|
|
|
49
49
|
import FuiApiController from "ui/controllers/fui_api_controller"
|
|
50
50
|
import FuiStateController from "ui/controllers/fui_state_controller"
|
|
51
51
|
import FuiVisibilityController from "ui/controllers/fui_visibility_controller"
|
|
52
|
+
import FuiEmojiPickerController from "ui/controllers/fui_emoji_picker_controller"
|
|
52
53
|
|
|
53
54
|
const controllers = {
|
|
54
55
|
"fui-site": FuiSiteController,
|
|
@@ -76,6 +77,7 @@ const controllers = {
|
|
|
76
77
|
"fui-api": FuiApiController,
|
|
77
78
|
"fui-state": FuiStateController,
|
|
78
79
|
"fui-visibility": FuiVisibilityController,
|
|
80
|
+
"fui-emoji-picker": FuiEmojiPickerController,
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
export function registerFuiControllers(application) {
|
|
@@ -111,4 +113,5 @@ export {
|
|
|
111
113
|
FuiApiController,
|
|
112
114
|
FuiStateController,
|
|
113
115
|
FuiVisibilityController,
|
|
116
|
+
FuiEmojiPickerController,
|
|
114
117
|
}
|
data/app/lib/component.rb
CHANGED
|
@@ -13,8 +13,16 @@ class Component
|
|
|
13
13
|
include ActiveModel::Model
|
|
14
14
|
include ActiveModel::Attributes
|
|
15
15
|
|
|
16
|
+
# HTML pass-through keys that bypass ActiveModel attributes
|
|
17
|
+
HTML_OPTIONS = %i[id class data style role tabindex title aria].to_set.freeze
|
|
18
|
+
|
|
16
19
|
class_attribute :slot_names, default: []
|
|
17
20
|
|
|
21
|
+
def initialize(**kwargs)
|
|
22
|
+
@html_options = kwargs.extract!(*HTML_OPTIONS)
|
|
23
|
+
super(**kwargs)
|
|
24
|
+
end
|
|
25
|
+
|
|
18
26
|
def self.default(**overrides)
|
|
19
27
|
overrides.each do |name, value|
|
|
20
28
|
type = attribute_types.fetch(name.to_s).type
|
|
@@ -49,6 +57,24 @@ class Component
|
|
|
49
57
|
|
|
50
58
|
private
|
|
51
59
|
|
|
60
|
+
# Merge component classes with html_options. Call from to_s:
|
|
61
|
+
# tag.div(**merge_html_options(class: classes)) { @content }
|
|
62
|
+
def merge_html_options(**opts)
|
|
63
|
+
html = @html_options || {}
|
|
64
|
+
return opts if html.empty?
|
|
65
|
+
|
|
66
|
+
merged = opts.merge(html)
|
|
67
|
+
# Merge classes together rather than overwriting
|
|
68
|
+
if opts[:class] && html[:class]
|
|
69
|
+
merged[:class] = "#{opts[:class]} #{html[:class]}"
|
|
70
|
+
end
|
|
71
|
+
# Deep merge data hashes
|
|
72
|
+
if opts[:data] && html[:data]
|
|
73
|
+
merged[:data] = opts[:data].merge(html[:data])
|
|
74
|
+
end
|
|
75
|
+
merged
|
|
76
|
+
end
|
|
77
|
+
|
|
52
78
|
def tag
|
|
53
79
|
@view_context.tag
|
|
54
80
|
end
|
data/config/importmap.rb
CHANGED
|
@@ -25,3 +25,6 @@ pin "ui/controllers/fui_tab_controller", to: "ui/controllers/fui_tab_controller.
|
|
|
25
25
|
pin "ui/controllers/fui_toast_controller", to: "ui/controllers/fui_toast_controller.js"
|
|
26
26
|
pin "ui/controllers/fui_transition_controller", to: "ui/controllers/fui_transition_controller.js"
|
|
27
27
|
pin "ui/controllers/fui_visibility_controller", to: "ui/controllers/fui_visibility_controller.js"
|
|
28
|
+
pin "ui/controllers/fui_emoji_picker_controller", to: "ui/controllers/fui_emoji_picker_controller.js"
|
|
29
|
+
|
|
30
|
+
pin "emoji-picker-element", to: "https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"
|
data/lib/ui/version.rb
CHANGED
data/lib/ui.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails-active-ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- nathan
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-03-26 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: rails
|
|
@@ -44,6 +45,7 @@ files:
|
|
|
44
45
|
- app/components/api_component.rb
|
|
45
46
|
- app/components/breadcrumb_component.rb
|
|
46
47
|
- app/components/button_component.rb
|
|
48
|
+
- app/components/button_to_component.rb
|
|
47
49
|
- app/components/calendar_component.rb
|
|
48
50
|
- app/components/card_component.rb
|
|
49
51
|
- app/components/checkbox_component.rb
|
|
@@ -103,6 +105,7 @@ files:
|
|
|
103
105
|
- app/components/step_component.rb
|
|
104
106
|
- app/components/step_group_component.rb
|
|
105
107
|
- app/components/sticky_component.rb
|
|
108
|
+
- app/components/style_component.rb
|
|
106
109
|
- app/components/sub_header_component.rb
|
|
107
110
|
- app/components/sub_menu_component.rb
|
|
108
111
|
- app/components/tab_component.rb
|
|
@@ -114,6 +117,7 @@ files:
|
|
|
114
117
|
- app/components/transition_component.rb
|
|
115
118
|
- app/components/v_stack_component.rb
|
|
116
119
|
- app/components/visibility_component.rb
|
|
120
|
+
- app/components/wrapper_component.rb
|
|
117
121
|
- app/helpers/component_helper.rb
|
|
118
122
|
- app/helpers/fui_helper.rb
|
|
119
123
|
- app/javascript/accordion.js
|
|
@@ -173,6 +177,7 @@ files:
|
|
|
173
177
|
- app/javascript/ui/controllers/fui_dimmer_controller.js
|
|
174
178
|
- app/javascript/ui/controllers/fui_dropdown_controller.js
|
|
175
179
|
- app/javascript/ui/controllers/fui_embed_controller.js
|
|
180
|
+
- app/javascript/ui/controllers/fui_emoji_picker_controller.js
|
|
176
181
|
- app/javascript/ui/controllers/fui_flyout_controller.js
|
|
177
182
|
- app/javascript/ui/controllers/fui_form_controller.js
|
|
178
183
|
- app/javascript/ui/controllers/fui_modal_controller.js
|
|
@@ -198,7 +203,7 @@ files:
|
|
|
198
203
|
- config/importmap.rb
|
|
199
204
|
- config/initializers/ruby_template_handler.rb
|
|
200
205
|
- config/routes.rb
|
|
201
|
-
- lib/
|
|
206
|
+
- lib/rails_active_ui.rb
|
|
202
207
|
- lib/tasks/ui_tasks.rake
|
|
203
208
|
- lib/ui.rb
|
|
204
209
|
- lib/ui/engine.rb
|
|
@@ -210,6 +215,7 @@ metadata:
|
|
|
210
215
|
homepage_uri: https://github.com/n-at-han-k/rails-active-ui
|
|
211
216
|
source_code_uri: https://github.com/n-at-han-k/rails-active-ui/tree/main
|
|
212
217
|
changelog_uri: https://github.com/n-at-han-k/rails-active-ui/blob/main/CHANGELOG.md
|
|
218
|
+
post_install_message:
|
|
213
219
|
rdoc_options: []
|
|
214
220
|
require_paths:
|
|
215
221
|
- lib
|
|
@@ -224,7 +230,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
224
230
|
- !ruby/object:Gem::Version
|
|
225
231
|
version: '0'
|
|
226
232
|
requirements: []
|
|
227
|
-
rubygems_version: 3.
|
|
233
|
+
rubygems_version: 3.3.15
|
|
234
|
+
signing_key:
|
|
228
235
|
specification_version: 4
|
|
229
236
|
summary: ActiveModel::Attributes-based view components
|
|
230
237
|
test_files: []
|