fluxbit_view_components 0.2.0 → 0.3.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/app/components/fluxbit/form/check_box_component.rb +56 -0
- data/app/components/fluxbit/form/component.rb +18 -24
- data/app/components/fluxbit/form/dropzone_component.html.erb +39 -0
- data/app/components/fluxbit/form/dropzone_component.rb +39 -0
- data/app/components/fluxbit/form/field_component.rb +26 -0
- data/app/components/fluxbit/form/form_builder_component.rb +1 -1
- data/app/components/fluxbit/form/{helper_text_component.rb → help_text_component.rb} +8 -3
- data/app/components/fluxbit/form/label_component.rb +32 -29
- data/app/components/fluxbit/form/range_component.rb +52 -0
- data/app/components/fluxbit/form/select_component.rb +88 -0
- data/app/components/fluxbit/form/text_field_component.rb +168 -0
- data/app/components/fluxbit/form/toggle_component.html.erb +23 -0
- data/app/components/fluxbit/form/toggle_component.rb +81 -0
- data/app/components/fluxbit/form/upload_image_component.html.erb +50 -0
- data/app/components/fluxbit/form/upload_image_component.rb +50 -0
- data/app/helpers/fluxbit/components_helper.rb +23 -51
- data/app/helpers/fluxbit/form_builder.rb +87 -0
- data/lib/fluxbit/config/form/check_box_component.rb +19 -0
- data/lib/fluxbit/config/form/dropzone_component.rb +20 -0
- data/lib/fluxbit/config/form/{helper_text_component.rb → help_text_component.rb} +1 -1
- data/lib/fluxbit/config/form/label_component.rb +30 -0
- data/lib/fluxbit/config/form/range_component.rb +15 -0
- data/lib/fluxbit/config/form/text_field_component.rb +76 -0
- data/{app/components/fluxbit/form/toggle_input_component.rb → lib/fluxbit/config/form/toggle_component.rb} +28 -115
- data/lib/fluxbit/view_components/version.rb +1 -1
- data/lib/fluxbit/view_components.rb +7 -1
- data/lib/install/install.rb +3 -3
- metadata +21 -18
- data/LICENSE.txt +0 -20
- data/app/components/fluxbit/form/checkbox_input_component.rb +0 -61
- data/app/components/fluxbit/form/datepicker_component.rb +0 -7
- data/app/components/fluxbit/form/radio_input_component.rb +0 -21
- data/app/components/fluxbit/form/range_input_component.rb +0 -51
- data/app/components/fluxbit/form/select_free_input_component.rb +0 -77
- data/app/components/fluxbit/form/select_input_component.rb +0 -21
- data/app/components/fluxbit/form/spacer_input_component.rb +0 -12
- data/app/components/fluxbit/form/text_input_component.rb +0 -225
- data/app/components/fluxbit/form/textarea_input_component.rb +0 -57
- data/app/components/fluxbit/form/upload_image_input_component.html.erb +0 -48
- data/app/components/fluxbit/form/upload_image_input_component.rb +0 -61
- data/app/components/fluxbit/form/upload_input_component.html.erb +0 -12
- data/app/components/fluxbit/form/upload_input_component.rb +0 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f5ca4fde5d32556cdbc0f25088912fa549531dea43e9e46557806c742a61a3c
|
4
|
+
data.tar.gz: 2feca2ea7274688b6d1e850aee6dc6d5638718718584e977514f0abf96cedd8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85ef792f0b9ead019bf89459531723043c7b771fe7b6c8f016ba03697d2e80d93139a081e0f4250d46545eb97b31f159fcaa9ba2bd5592c27053a0792c8063f5
|
7
|
+
data.tar.gz: da59d2153f6ea5bb116ee44ae2dd7fd8cd8631c9024ebe7972d5103eb58f1fc05012953386d4520aed4247970dc00fc810ede2afc08a0ecc8329db053b3d5cb6
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The `Fluxbit::Form::CheckBoxComponent` is a form input component for check boxes and radio buttons.
|
4
|
+
# It extends `Fluxbit::Form::FieldComponent` and provides a styled checkbox/radio with label, helper text,
|
5
|
+
# and support for different visual states and groupings. It automatically adds the correct styles for both
|
6
|
+
# checkbox and radio types and works with or without Rails form builders.
|
7
|
+
#
|
8
|
+
# @example Basic usage
|
9
|
+
# = render Fluxbit::Form::CheckBoxComponent.new(name: :accept_terms, label: "Accept the terms")
|
10
|
+
#
|
11
|
+
# @see docs/03_Forms/CheckBox.md For detailed documentation and examples.
|
12
|
+
class Fluxbit::Form::CheckBoxComponent < Fluxbit::Form::FieldComponent
|
13
|
+
include Fluxbit::Config::Form::CheckBoxComponent
|
14
|
+
TYPE_DEFAULT = :check_box
|
15
|
+
TYPE_OPTIONS = %i[check_box checkbox radio_button].freeze
|
16
|
+
|
17
|
+
# Initializes the check box component with the given properties.
|
18
|
+
#
|
19
|
+
# @param name [String] Name of the field (required unless using form builder)
|
20
|
+
# @param label [String] Label text next to the input (optional)
|
21
|
+
# @param value [String] Value for the field (optional)
|
22
|
+
# @param type [String, Symbol] Input type (`"check_box"`, `"checkbox"`, `"radio_button"`)
|
23
|
+
# @param help_text [String] Helper or error text below the field
|
24
|
+
# @param disabled [Boolean] Disables the input if true
|
25
|
+
# @param checked [Boolean] Marks the input as checked if true
|
26
|
+
# @param class [String] Additional CSS classes for the input element
|
27
|
+
# @param ... any other HTML attribute supported by check_box_tag/radio_button_tag
|
28
|
+
def initialize(**props)
|
29
|
+
super(**props)
|
30
|
+
@type = options(@props.delete(:type), collection: TYPE_OPTIONS, default: TYPE_DEFAULT)
|
31
|
+
add(class: styles[:checkbox], to: @props, first_element: true) if @props[:type] == "checkbox"
|
32
|
+
add(class: styles[:base], to: @props, first_element: true)
|
33
|
+
end
|
34
|
+
|
35
|
+
def input
|
36
|
+
if @form.present? && @attribute.present?
|
37
|
+
@form.public_send(@type, @attribute, @props)
|
38
|
+
else
|
39
|
+
public_send("#{@type}_tag", @name, @value, @props)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def call
|
44
|
+
if @help_text
|
45
|
+
content_tag :div, { class: "flex" } do
|
46
|
+
concat content_tag(:div, input, { class: styles[:input_div] })
|
47
|
+
concat content_tag(:div, safe_join([ label, help_text ]), { class: styles[:helper_div] })
|
48
|
+
end
|
49
|
+
else
|
50
|
+
content_tag :div, { class: styles[:no_helper_div] } do
|
51
|
+
concat input
|
52
|
+
concat label
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -6,32 +6,32 @@ class Fluxbit::Form::Component < Fluxbit::Component
|
|
6
6
|
return @id ||= random_id if @props[:id].nil? && @form.nil?
|
7
7
|
return @props[:id] unless @props[:id].nil?
|
8
8
|
|
9
|
-
"#{@form.object_name}_#{@
|
9
|
+
"#{@form.object_name}_#{@attribute}"
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
return nil if
|
12
|
+
def define_help_text(help_text, object, attribute)
|
13
|
+
return nil if help_text.is_a? FalseClass
|
14
14
|
|
15
|
-
if
|
16
|
-
|
17
|
-
|
18
|
-
scope: [ :activerecord, :
|
15
|
+
if help_text.nil? && !object.nil? && !attribute.nil?
|
16
|
+
help_text = I18n.t(
|
17
|
+
attribute,
|
18
|
+
scope: [ :activerecord, :help_text, object.class.name.underscore.to_sym ],
|
19
19
|
default: nil
|
20
20
|
)
|
21
21
|
end
|
22
22
|
|
23
|
-
(
|
23
|
+
(help_text.is_a?(Array) ? help_text : [ help_text ]) + errors
|
24
24
|
end
|
25
25
|
|
26
|
-
def define_helper_popover(helper_popover, object,
|
26
|
+
def define_helper_popover(helper_popover, object, attribute)
|
27
27
|
return helper_popover if (helper_popover != false && !helper_popover.nil?) || object.nil?
|
28
28
|
|
29
29
|
object_name = object.class.name.underscore.to_sym
|
30
|
-
I18n.t(
|
30
|
+
I18n.t(attribute, scope: [ :activerecord, :helper_popover, object_name ], default: nil)
|
31
31
|
end
|
32
32
|
|
33
|
-
def label_value(label, object,
|
34
|
-
return object.class.human_attribute_name(
|
33
|
+
def label_value(label, object, attribute, id)
|
34
|
+
return object.class.human_attribute_name(attribute) if label.nil? && !object.nil? && !attribute.nil?
|
35
35
|
return id.to_s.humanize if label.nil? && !id.nil?
|
36
36
|
return label unless label.nil?
|
37
37
|
|
@@ -53,20 +53,14 @@ class Fluxbit::Form::Component < Fluxbit::Component
|
|
53
53
|
def errors
|
54
54
|
return [] unless @object&.errors&.any?
|
55
55
|
|
56
|
-
@object.errors.filter { |f| f.attribute == @
|
56
|
+
@object.errors.filter { |f| f.attribute == @attribute }.map(&:full_message)
|
57
57
|
end
|
58
58
|
|
59
|
-
def
|
60
|
-
return "" if @
|
59
|
+
def help_text
|
60
|
+
return "" if @help_text.blank? || @help_text.compact.blank?
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
# end
|
66
|
-
# )
|
67
|
-
|
68
|
-
@helper_text.compact.map do |text|
|
69
|
-
concat Fluxbit::Form::HelperTextComponent.new(color: @color).with_content(text).render_in(view_context)
|
70
|
-
end
|
62
|
+
@help_text.compact.map do |text|
|
63
|
+
Fluxbit::Form::HelpTextComponent.new(color: @color).with_content(text).render_in(view_context)
|
64
|
+
end.join.html_safe
|
71
65
|
end
|
72
66
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<%= content_tag :div, **@wrapper_html do %>
|
2
|
+
<div class="<%= self.styles[:base] %>">
|
3
|
+
<label for="<%= id %>" class="<%= self.styles[:label] %> <%= self.styles[:height][@height] %>">
|
4
|
+
<div class="<%= self.styles[:inner_div] %>">
|
5
|
+
<% if content? %>
|
6
|
+
<%= content %>
|
7
|
+
<% else %>
|
8
|
+
<%= create_icon %>
|
9
|
+
|
10
|
+
<% if @title != false %>
|
11
|
+
<p class="<%= self.styles[:title] %>">
|
12
|
+
<% if @title != true %>
|
13
|
+
<%= @title %>
|
14
|
+
<% else %>
|
15
|
+
<span class="font-semibold">Click to upload</span> or drag and drop
|
16
|
+
<% end %>
|
17
|
+
</p>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% if @subtitle != false %>
|
21
|
+
<p class="<%= self.styles[:subtitle] %>">
|
22
|
+
<% if @subtitle != true %>
|
23
|
+
<%= @subtitle %>
|
24
|
+
<% else %>
|
25
|
+
SVG, PNG, JPG or GIF (MAX. 800x400px)
|
26
|
+
<% end %>
|
27
|
+
</p>
|
28
|
+
<% end %>
|
29
|
+
<% end %>
|
30
|
+
</div>
|
31
|
+
<% if @form.present? && @attribute.present? %>
|
32
|
+
<%= @form.file_field(@attribute, **@props) %>
|
33
|
+
<% else %>
|
34
|
+
<%= file_field_tag(@name, **@props) %>
|
35
|
+
<% end %>
|
36
|
+
</label>
|
37
|
+
</div>
|
38
|
+
<%= help_text %>
|
39
|
+
<% end %>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The `Fluxbit::Form::DropzoneComponent` provides a drag-and-drop file input zone with support for labels,
|
4
|
+
# titles, subtitles, icons, validation states, and integration with Rails form builders.
|
5
|
+
# It renders a visually rich area that lets users drag files or click to select a file, and is fully customizable
|
6
|
+
# via its options and slot for custom content.
|
7
|
+
#
|
8
|
+
# @example Basic usage
|
9
|
+
# = render Fluxbit::Form::DropzoneComponent.new(name: :avatar)
|
10
|
+
#
|
11
|
+
# @see docs/03_Forms/Dropzone.md For detailed documentation and examples.
|
12
|
+
class Fluxbit::Form::DropzoneComponent < Fluxbit::Form::FieldComponent
|
13
|
+
include Fluxbit::Config::Form::DropzoneComponent
|
14
|
+
|
15
|
+
# Initializes the dropzone component with the given properties.
|
16
|
+
#
|
17
|
+
# @param name [String] Name of the field (required unless using form builder)
|
18
|
+
# @param label [String] Label for the input (optional)
|
19
|
+
# @param title [Boolean, String] Title text above the dropzone (true for default, false to hide, or custom string)
|
20
|
+
# @param subtitle [Boolean, String] Subtitle text below the title (true for default, false to hide, or custom string)
|
21
|
+
# @param icon [String, Symbol] Icon to display above the title (defaults to config)
|
22
|
+
# @param icon_props [Hash] Extra props for the icon element
|
23
|
+
# @param height [Integer] Height preset (0: auto, 1: h-32, 2: h-64, 3: h-96; default is 0)
|
24
|
+
# @param help_text [String] Helper or error text below the field
|
25
|
+
# @param ... any other HTML attribute supported by file_field_tag
|
26
|
+
def initialize(**props)
|
27
|
+
super(**props)
|
28
|
+
@title = options(@props.delete(:title), default: true)
|
29
|
+
@subtitle = options(@props.delete(:subtitle), default: true)
|
30
|
+
@icon = @props.delete(:icon) || @@icon
|
31
|
+
@icon_props = @props.delete(:icon_props) || { class: styles[:icon] }
|
32
|
+
@height = @props.delete(:height) || @@height
|
33
|
+
add to: @props, class: "hidden"
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_icon
|
37
|
+
anyicon(icon: @icon, class: styles[:icon])
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Fluxbit::Form::FieldComponent < Fluxbit::Form::Component
|
4
|
+
def initialize(**props)
|
5
|
+
super
|
6
|
+
@props = props
|
7
|
+
@form = @props.delete(:form)
|
8
|
+
@attribute = @props.delete(:attribute)
|
9
|
+
@name = @props.delete(:name) || (@attribute if @form.present?)
|
10
|
+
@value = @props.delete(:value)
|
11
|
+
@id = @props.delete(:id)
|
12
|
+
|
13
|
+
@object = @form&.object
|
14
|
+
@help_text = define_help_text(props.delete(:help_text), @object, @attribute)
|
15
|
+
@helper_popover = define_helper_popover(props.delete(:helper_popover), @object, @attribute)
|
16
|
+
@helper_popover_placement = props.delete(:helper_popover_placement) || "right"
|
17
|
+
@label = label_value(props.delete(:label), @object, @attribute, @id)
|
18
|
+
@wrapper_html = props.delete(:wrapper_html) || {}
|
19
|
+
define_wrapper_options
|
20
|
+
end
|
21
|
+
|
22
|
+
def define_wrapper_options
|
23
|
+
add(to: @wrapper_html, class: "required") if @props[:required].present?
|
24
|
+
add(to: @wrapper_html, class: @name) if @name.present?
|
25
|
+
end
|
26
|
+
end
|
@@ -89,7 +89,7 @@ class Fluxbit::Form::FormBuilderComponent < Fluxbit::Component
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def element_type(type)
|
92
|
-
return "
|
92
|
+
return "TextField" if type.nil? || type.to_s.in?(TEXT_TYPES)
|
93
93
|
return type.to_s.concat("_input").camelcase if type.to_s.in?(INPUT_TYPES)
|
94
94
|
|
95
95
|
case type
|
@@ -1,11 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# The `Fluxbit::
|
3
|
+
# The `Fluxbit::HelpTextComponent` is a component for rendering customizable helper text elements.
|
4
4
|
# It extends `Fluxbit::Component` and provides options for configuring the helper text's
|
5
5
|
# appearance and behavior. You can control the helper text's color and other attributes.
|
6
6
|
# The helper text can have various styles applied based on the provided properties.
|
7
|
-
|
8
|
-
|
7
|
+
#
|
8
|
+
# @example Basic usage
|
9
|
+
# = render Fluxbit::Form::HelpTextComponent.new { "Your password must be at least 8 characters." }
|
10
|
+
#
|
11
|
+
# @see docs/03_Forms/HelpText.md For detailed documentation and examples.
|
12
|
+
class Fluxbit::Form::HelpTextComponent < Fluxbit::Form::Component
|
13
|
+
include Fluxbit::Config::Form::HelpTextComponent
|
9
14
|
|
10
15
|
# Initializes the helper text component with the given properties.
|
11
16
|
#
|
@@ -1,36 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# The `Fluxbit::Form::LabelComponent` is a flexible and accessible label for form fields.
|
4
|
+
# It supports custom content, helper popovers, multiple color styles, sizing options, and
|
5
|
+
# displays associated help text when provided. It is fully compatible with Rails form builders.
|
6
|
+
#
|
7
|
+
# @example Basic usage
|
8
|
+
# = render Fluxbit::Form::LabelComponent.new(with_content: "Your Name")
|
9
|
+
#
|
10
|
+
# @see docs/03_Forms/Label.md For detailed documentation and examples.
|
3
11
|
class Fluxbit::Form::LabelComponent < Fluxbit::Form::Component
|
4
|
-
|
5
|
-
{
|
6
|
-
base: "flex font-medium",
|
7
|
-
colors: {
|
8
|
-
default: "text-gray-900 dark:text-white",
|
9
|
-
success: "text-green-700 dark:text-green-500",
|
10
|
-
failure: "text-red-700 dark:text-red-500",
|
11
|
-
info: "text-cyan-500 dark:text-cyan-600",
|
12
|
-
warning: "text-yellow-500 dark:text-yellow-600"
|
13
|
-
},
|
14
|
-
sizes: {
|
15
|
-
sm: "text-sm",
|
16
|
-
md: "text-md",
|
17
|
-
lg: "text-lg"
|
18
|
-
},
|
19
|
-
helper_popover: "px-2 text-slate-400"
|
20
|
-
}
|
21
|
-
end
|
12
|
+
include Fluxbit::Config::Form::LabelComponent
|
22
13
|
|
23
|
-
|
24
|
-
|
14
|
+
# Initializes the label component with the given properties.
|
15
|
+
#
|
16
|
+
# @param with_content [String] The label text to display (alternative to block content)
|
17
|
+
# @param help_text [String, Array<String>] One or more help text messages to render below the label
|
18
|
+
# @param helper_popover [String] Popover content shown on icon hover
|
19
|
+
# @param helper_popover_placement [String] Placement of the popover (default: "right")
|
20
|
+
# @param sizing [Integer] Size index for label text (default: config default)
|
21
|
+
# @param color [Symbol] Label color (:default, :success, :failure, :info, :warning)
|
22
|
+
# @param class [String] Additional CSS classes for the label element
|
23
|
+
# @param ... any other HTML attribute supported by the <label> tag
|
24
|
+
def initialize(**props)
|
25
25
|
super
|
26
26
|
@props = props
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
30
|
-
@helper_popover = helper_popover
|
31
|
-
@helper_popover_placement = helper_popover_placement
|
32
|
-
|
33
|
-
|
27
|
+
@with_content = @props.delete(:with_content)
|
28
|
+
@help_text = @props.delete(:help_text)
|
29
|
+
@help_text = [ @help_text ] if !@help_text.is_a?(Array)
|
30
|
+
@helper_popover = @props.delete(:helper_popover)
|
31
|
+
@helper_popover_placement = @props.delete(:helper_popover_placement) || @@helper_popover_placement
|
32
|
+
@sizing = @props[:sizing].to_i || @@sizing
|
33
|
+
@sizing = (styles[:sizes].count - 1) if @sizing > (styles[:sizes].count - 1)
|
34
|
+
@color = options(@props.delete(:color), collection: styles[:colors], default: @@color)
|
35
|
+
|
36
|
+
add class: styles[:colors][@color], to: @props, first_element: true
|
34
37
|
add class: styles[:base], to: @props, first_element: true
|
35
38
|
add class: styles[:sizes][@sizing], to: @props, first_element: true
|
36
39
|
end
|
@@ -39,7 +42,7 @@ class Fluxbit::Form::LabelComponent < Fluxbit::Form::Component
|
|
39
42
|
return "" if @helper_popover.nil?
|
40
43
|
|
41
44
|
content_tag :span,
|
42
|
-
anyicon(icon:
|
45
|
+
anyicon(icon: @@helper_popover_icon, class: @@helper_popover_icon_class),
|
43
46
|
{
|
44
47
|
"data-popover-placement": @helper_popover_placement,
|
45
48
|
"data-popover-target": target,
|
@@ -57,7 +60,7 @@ class Fluxbit::Form::LabelComponent < Fluxbit::Form::Component
|
|
57
60
|
safe_join(
|
58
61
|
[
|
59
62
|
content_tag(:label, safe_join([ content || @with_content, span_helper_popover ]), @props),
|
60
|
-
|
63
|
+
help_text,
|
61
64
|
render_popover
|
62
65
|
]
|
63
66
|
)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The `Fluxbit::Form::RangeComponent` renders a styled range slider for selecting numeric values within a range.
|
4
|
+
# It supports vertical and horizontal orientation, sizing options, helper text, labels, and full compatibility with Rails form builders.
|
5
|
+
# Custom classes and HTML attributes can be passed for further styling and control.
|
6
|
+
#
|
7
|
+
# @example Basic usage
|
8
|
+
# = render Fluxbit::Form::RangeComponent.new(name: :volume, label: "Volume")
|
9
|
+
#
|
10
|
+
# @see docs/03_Forms/Range.md For detailed documentation and examples.
|
11
|
+
class Fluxbit::Form::RangeComponent < Fluxbit::Form::FieldComponent
|
12
|
+
include Fluxbit::Config::Form::RangeComponent
|
13
|
+
|
14
|
+
# Initializes the range component with the given properties.
|
15
|
+
#
|
16
|
+
# @param name [String] Name of the field (required unless using form builder)
|
17
|
+
# @param label [String] Label for the input (optional)
|
18
|
+
# @param value [Numeric] Value for the range input (optional)
|
19
|
+
# @param min [Numeric] Minimum value for the range slider (optional)
|
20
|
+
# @param max [Numeric] Maximum value for the range slider (optional)
|
21
|
+
# @param step [Numeric] Step value for the slider (optional)
|
22
|
+
# @param vertical [Boolean] Renders the slider vertically if true (default: false)
|
23
|
+
# @param sizing [Integer] Size index for slider height/thickness (default: config default)
|
24
|
+
# @param help_text [String] Helper or error text below the field
|
25
|
+
# @param class [String] Additional CSS classes for the input element
|
26
|
+
# @param ... any other HTML attribute supported by the <input type="range"> tag
|
27
|
+
def initialize(**props)
|
28
|
+
super(**props)
|
29
|
+
@vertical = options(@props.delete(:vertical), collection: [ true, false ], default: @@vertical)
|
30
|
+
@sizing = @props[:sizing].to_i || @@sizing
|
31
|
+
@sizing = (styles[:sizes].count - 1) if @sizing > (styles[:sizes].count - 1)
|
32
|
+
@props[:type] = "range"
|
33
|
+
@props[:style] = @props[:style] || "" + ";transform: rotate(270deg);" if @vertical
|
34
|
+
|
35
|
+
add(class: styles[:sizes][@sizing], to: @props, first_element: true)
|
36
|
+
add(class: styles[:base], to: @props, first_element: true)
|
37
|
+
end
|
38
|
+
|
39
|
+
def range
|
40
|
+
if @form.nil?
|
41
|
+
text_field_tag @name, @value, @props
|
42
|
+
else
|
43
|
+
@form.text_field(@attribute, **@props)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def call
|
48
|
+
content_tag :div, **@wrapper_html do
|
49
|
+
safe_join [ label, range, help_text ]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The `Fluxbit::Form::SelectComponent` is a styled dropdown/select field for forms.
|
4
|
+
# It supports standard, grouped, and time zone options, integrates with Rails form builders,
|
5
|
+
# and provides flexible props for prompt, disabled/selected options, helper text, and more.
|
6
|
+
#
|
7
|
+
# @example Basic usage
|
8
|
+
# = render Fluxbit::Form::SelectComponent.new(name: :role, options: ["Admin", "User", "Guest"], label: "User Role")
|
9
|
+
#
|
10
|
+
# @see docs/03_Forms/Select.md For detailed documentation and examples.
|
11
|
+
class Fluxbit::Form::SelectComponent < Fluxbit::Form::TextFieldComponent
|
12
|
+
# Initializes the select component with the given properties.
|
13
|
+
#
|
14
|
+
# @param name [String] Name of the field (required unless using form builder)
|
15
|
+
# @param label [String] Label for the input (optional)
|
16
|
+
# @param value [String] Value for the field (optional)
|
17
|
+
# @param grouped [Boolean] Enables grouped select options (default: false)
|
18
|
+
# @param time_zone [Boolean] Uses Rails time zone select options (default: false)
|
19
|
+
# @param select_options [Hash] Options for select tag (prompt, selected, disabled, etc)
|
20
|
+
# @param choices [Array] List of choices for options (alternative to options)
|
21
|
+
# @param options [Array, Hash] List or hash of options (or groups if grouped)
|
22
|
+
# @param help_text [String] Helper or error text below the field
|
23
|
+
# @param class [String] Additional CSS classes for the select element
|
24
|
+
# @param ... any other HTML attribute supported by <select>
|
25
|
+
def initialize(**props)
|
26
|
+
super(**props)
|
27
|
+
@grouped = @props.delete(:grouped) || false
|
28
|
+
@time_zone = @props.delete(:time_zone) || false
|
29
|
+
@select_options = @props.delete(:select_options) || {}
|
30
|
+
@choices = @props.delete(:choices) || nil
|
31
|
+
@options = @props.delete(:options) || {}
|
32
|
+
@options = ::ActiveSupport::TimeZone.all if @time_zone
|
33
|
+
end
|
34
|
+
|
35
|
+
def input
|
36
|
+
if @form.present? && @attribute.present?
|
37
|
+
@form.select(
|
38
|
+
@attribute,
|
39
|
+
build_options_for_select,
|
40
|
+
@select_options,
|
41
|
+
@props
|
42
|
+
)
|
43
|
+
else
|
44
|
+
select_tag(
|
45
|
+
@name,
|
46
|
+
build_options_for_select,
|
47
|
+
@props
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_options_for_select
|
53
|
+
if @grouped
|
54
|
+
grouped_options_for_select(
|
55
|
+
@options,
|
56
|
+
@selected,
|
57
|
+
disabled: @disabled_options,
|
58
|
+
prompt: @prompt,
|
59
|
+
divider: @divider
|
60
|
+
)
|
61
|
+
elsif @time_zone
|
62
|
+
time_zone_options_for_select(@selected)
|
63
|
+
else
|
64
|
+
options_for_select(@options, selected: @selected, disabled: @disabled_options)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def grouped_selected_option
|
71
|
+
@options.each do |group|
|
72
|
+
group_to_traverse = @divider ? group[1] : group
|
73
|
+
if group_to_traverse.is_a?(String)
|
74
|
+
return group_to_traverse if group_to_traverse == @selected.to_s
|
75
|
+
|
76
|
+
next
|
77
|
+
end
|
78
|
+
|
79
|
+
group_to_traverse.each do |item|
|
80
|
+
if item.is_a?(Array) && item[1] == @selected.to_s
|
81
|
+
return item[0]
|
82
|
+
elsif item.is_a?(String) && item == @selected.to_s
|
83
|
+
return item
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The `Fluxbit::Form::TextFieldComponent` is a form input component that extends `Fluxbit::Form::FieldComponent`.
|
4
|
+
# It provides a styled text input (or textarea) with support for various HTML input types, optional icons or add-on content,
|
5
|
+
# and color-coded validation states (e.g. default, success, error).
|
6
|
+
#
|
7
|
+
# @example Basic usage
|
8
|
+
# = render Fluxbit::Form::TextFieldComponent.new(name: :email)
|
9
|
+
#
|
10
|
+
# @see docs/03_Forms/TextField.md For detailed documentation and examples.
|
11
|
+
class Fluxbit::Form::TextFieldComponent < Fluxbit::Form::FieldComponent
|
12
|
+
TYPE_DEFAULT = :text
|
13
|
+
TYPE_OPTIONS = %i[text textarea text_area color number email password search tel url date datetime_local month time week currency]
|
14
|
+
include Fluxbit::Config::Form::TextFieldComponent
|
15
|
+
|
16
|
+
# Initializes the text field component with the given properties.
|
17
|
+
#
|
18
|
+
# @param form [ActionView::Helpers::FormBuilder] The form builder (optional, for Rails forms)
|
19
|
+
# @param attribute [Symbol] The model attribute to be used in the form (required if using form builder)
|
20
|
+
# @param id [String] The id of the input element (optional)
|
21
|
+
# @param label [String] The label for the input field (optional)
|
22
|
+
# @param help_text [String] Additional help text for the input field (optional)
|
23
|
+
# @param helper_popover [String] Content for a popover helper (optional)
|
24
|
+
# @param helper_popover_placement [String] Placement of the popover (default: "right")
|
25
|
+
# @param name [String] Name of the field (required, unless using form builder)
|
26
|
+
# @param value [String] Value for the field (optional)
|
27
|
+
# @param type [Symbol] Input type (`:text`, `:email`, etc)
|
28
|
+
# @param icon [Symbol] Left icon (optional)
|
29
|
+
# @param right_icon [Symbol] Right icon (optional)
|
30
|
+
# @param addon [String] Add-on text or icon before the input (optional)
|
31
|
+
# @param addon_props [Hash] Props for the Add-on (optional)
|
32
|
+
# @param icon_props [Hash] Props for the left icon (optional)
|
33
|
+
# @param right_icon_props [Hash] Props for the right icon (optional)
|
34
|
+
# @param div_props [Hash] Props for the whole div (optional)
|
35
|
+
# @param multiline [Boolean] Renders a textarea if true
|
36
|
+
# @param color [Symbol] Field color (`:default`, `:success`, `:failure`, etc)
|
37
|
+
# @param sizing [Integer] Input size
|
38
|
+
# @param shadow [Boolean] Adds drop shadow if true
|
39
|
+
# @param disabled [Boolean] Disables the input if true
|
40
|
+
# @param readonly [Boolean] Makes the input readonly if true
|
41
|
+
# @param ... any other HTML attribute supported by input/textarea
|
42
|
+
def initialize(**props)
|
43
|
+
super(**props)
|
44
|
+
@color = valid_color(@props.delete(:color))
|
45
|
+
@type = options(@props.delete(:type), collection: TYPE_OPTIONS, default: TYPE_DEFAULT)
|
46
|
+
@icon = @props.delete(:icon)
|
47
|
+
@multiline = options(@props.delete(:multiline), default: false)
|
48
|
+
@shadow = @props.delete(:shadow)
|
49
|
+
@addon = @props.delete(:addon)
|
50
|
+
@right_icon = @props.delete(:right_icon)
|
51
|
+
@addon_props = @props.delete(:addon_props) || {}
|
52
|
+
@div_props = @props.delete(:div_props) || {}
|
53
|
+
@icon_props = @props.delete(:icon_props) || {}
|
54
|
+
@right_icon_props = @props.delete(:right_icon_props) || {}
|
55
|
+
@sizing = sizing_with_addon @props.delete(:sizing)
|
56
|
+
@props[:type] = @type
|
57
|
+
|
58
|
+
declare_classes
|
59
|
+
@props[:class] = remove_class(@props.delete(:remove_class) || "", @props[:class])
|
60
|
+
end
|
61
|
+
|
62
|
+
def call
|
63
|
+
content_tag :div, **@wrapper_html do
|
64
|
+
safe_join [ label, icon_container, help_text ]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def valid_color(color)
|
71
|
+
return color if styles[:bg].key?(color)
|
72
|
+
return :failure if errors.present?
|
73
|
+
|
74
|
+
@@color
|
75
|
+
end
|
76
|
+
|
77
|
+
def sizing_with_addon(sizing)
|
78
|
+
sizing.to_i < styles[:sizes].count ? sizing.to_i : @@sizing
|
79
|
+
end
|
80
|
+
|
81
|
+
def declare_classes
|
82
|
+
add to: @props,
|
83
|
+
first_element: true,
|
84
|
+
class: [
|
85
|
+
styles[:default],
|
86
|
+
(@props.key?(:readonly) || @props.key?(:disabled) ? styles[:text][@color] : nil),
|
87
|
+
styles[:ring][@color],
|
88
|
+
styles[:bg][@color],
|
89
|
+
styles[:placeholder][@color],
|
90
|
+
styles[:border][@color],
|
91
|
+
@addon ? styles[:sizing_md_addon] : styles[:sizes][@sizing],
|
92
|
+
(@shadow ? styles[:shadow] : nil),
|
93
|
+
(@right_icon ? styles[:right_icon] : nil),
|
94
|
+
(@icon ? styles[:icon] : nil)
|
95
|
+
].compact.join(" ")
|
96
|
+
end
|
97
|
+
|
98
|
+
def icon(icon_v, tag: :div, props: nil)
|
99
|
+
return "" if icon_v.blank?
|
100
|
+
|
101
|
+
content_tag(
|
102
|
+
tag,
|
103
|
+
anyicon(
|
104
|
+
icon: icon_v,
|
105
|
+
class: styles[:additional_icons][:class][@color]
|
106
|
+
),
|
107
|
+
**props
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
def create_icon
|
112
|
+
add class: styles[:additional_icons][:icon], to: @icon_props
|
113
|
+
add(class: "pointer-events-none", to: @icon_props) unless events?(@icon_props)
|
114
|
+
icon(@icon, props: @icon_props)
|
115
|
+
end
|
116
|
+
|
117
|
+
def create_addon
|
118
|
+
add class: styles[:additional_icons][:addon][@color], to: @addon_props
|
119
|
+
icon(@addon, tag: :span, props: @addon_props)
|
120
|
+
end
|
121
|
+
|
122
|
+
def create_right_icon
|
123
|
+
add class: styles[:additional_icons][:right_icon], to: @right_icon_props
|
124
|
+
add(class: "pointer-events-none", to: @right_icon_props) unless events?(@right_icon_props)
|
125
|
+
icon(@right_icon, props: @right_icon_props)
|
126
|
+
end
|
127
|
+
|
128
|
+
def events?(props)
|
129
|
+
props.keys.intersection(
|
130
|
+
%i[onclick onsubmit onchange onkeydown onkeyup onkeypress href]
|
131
|
+
).present?
|
132
|
+
end
|
133
|
+
|
134
|
+
def input
|
135
|
+
input_type = case @type
|
136
|
+
when :text
|
137
|
+
@multiline ? "text_area" : "text_field"
|
138
|
+
when :tel then "telephone_field"
|
139
|
+
when :currency then "text_field"
|
140
|
+
when :textarea, :text_area then "text_area"
|
141
|
+
else
|
142
|
+
"#{@type}_field"
|
143
|
+
end
|
144
|
+
|
145
|
+
if @form.present? && @attribute.present?
|
146
|
+
@form.public_send(input_type, @attribute, @props)
|
147
|
+
else
|
148
|
+
public_send("#{input_type}_tag", @name, @value, @props)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def icon_container_with_addon
|
153
|
+
add class: "flex", to: @div_props
|
154
|
+
content_tag :div, safe_join([ create_addon, create_right_icon, input ]), @div_props
|
155
|
+
end
|
156
|
+
|
157
|
+
def icon_container_without_addon
|
158
|
+
add class: "relative w-full", to: @div_props
|
159
|
+
content_tag :div, safe_join([ create_icon, create_right_icon, input ]), @div_props
|
160
|
+
end
|
161
|
+
|
162
|
+
def icon_container
|
163
|
+
return input if @icon.nil? && @right_icon.nil? && @addon.nil?
|
164
|
+
return icon_container_with_addon unless @addon.nil?
|
165
|
+
|
166
|
+
icon_container_without_addon
|
167
|
+
end
|
168
|
+
end
|