better_ui 0.3.0 → 0.7.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 +257 -212
- data/Rakefile +11 -2
- data/app/components/better_ui/action_messages_component/action_messages_component.html.erb +48 -0
- data/app/components/better_ui/action_messages_component.rb +544 -0
- data/app/components/better_ui/application_component.rb +66 -0
- data/app/components/better_ui/button_component/button_component.html.erb +31 -0
- data/app/components/better_ui/button_component.rb +307 -0
- data/app/components/better_ui/card_component/card_component.html.erb +17 -0
- data/app/components/better_ui/card_component.rb +460 -0
- data/app/components/better_ui/drawer/header_component/header_component.html.erb +24 -0
- data/app/components/better_ui/drawer/header_component.rb +238 -0
- data/app/components/better_ui/drawer/layout_component/layout_component.html.erb +44 -0
- data/app/components/better_ui/drawer/layout_component.rb +270 -0
- data/app/components/better_ui/drawer/nav_group_component/nav_group_component.html.erb +10 -0
- data/app/components/better_ui/drawer/nav_group_component.rb +155 -0
- data/app/components/better_ui/drawer/nav_item_component/nav_item_component.html.erb +13 -0
- data/app/components/better_ui/drawer/nav_item_component.rb +225 -0
- data/app/components/better_ui/drawer/sidebar_component/sidebar_component.html.erb +17 -0
- data/app/components/better_ui/drawer/sidebar_component.rb +263 -0
- data/app/components/better_ui/forms/base_component.rb +450 -0
- data/app/components/better_ui/forms/checkbox_component/checkbox_component.html.erb +28 -0
- data/app/components/better_ui/forms/checkbox_component.rb +419 -0
- data/app/components/better_ui/forms/checkbox_group_component/checkbox_group_component.html.erb +40 -0
- data/app/components/better_ui/forms/checkbox_group_component.rb +363 -0
- data/app/components/better_ui/forms/number_input_component/number_input_component.html.erb +40 -0
- data/app/components/better_ui/forms/number_input_component.rb +320 -0
- data/app/components/better_ui/forms/password_input_component/password_input_component.html.erb +71 -0
- data/app/components/better_ui/forms/password_input_component.rb +206 -0
- data/app/components/better_ui/forms/text_input_component/text_input_component.html.erb +40 -0
- data/app/components/better_ui/forms/text_input_component.rb +258 -0
- data/app/components/better_ui/forms/textarea_component/textarea_component.html.erb +40 -0
- data/app/components/better_ui/forms/textarea_component.rb +329 -0
- data/app/form_builders/better_ui/ui_form_builder.rb +467 -0
- data/app/helpers/better_ui/application_helper.rb +325 -51
- data/app/views/layouts/better_ui/application.html.erb +1 -1
- data/config/routes.rb +1 -0
- data/lib/better_ui/engine.rb +34 -5
- data/lib/better_ui/version.rb +1 -1
- data/lib/better_ui.rb +32 -4
- data/lib/generators/better_ui/install/USAGE +44 -0
- data/lib/generators/better_ui/install/install_generator.rb +87 -0
- data/lib/generators/better_ui/install/templates/better_ui_theme.css.tt +280 -0
- data/lib/tasks/better_ui_tasks.rake +39 -4
- metadata +52 -185
- data/app/components/better_ui/application/card/component.html.erb +0 -20
- data/app/components/better_ui/application/card/component.rb +0 -214
- data/app/components/better_ui/application/main/component.html.erb +0 -9
- data/app/components/better_ui/application/main/component.rb +0 -123
- data/app/components/better_ui/application/navbar/component.html.erb +0 -92
- data/app/components/better_ui/application/navbar/component.rb +0 -136
- data/app/components/better_ui/application/sidebar/component.html.erb +0 -227
- data/app/components/better_ui/application/sidebar/component.rb +0 -130
- data/app/components/better_ui/general/accordion/component.html.erb +0 -5
- data/app/components/better_ui/general/accordion/component.rb +0 -92
- data/app/components/better_ui/general/accordion/item_component.html.erb +0 -12
- data/app/components/better_ui/general/accordion/item_component.rb +0 -176
- data/app/components/better_ui/general/alert/component.html.erb +0 -32
- data/app/components/better_ui/general/alert/component.rb +0 -242
- data/app/components/better_ui/general/avatar/component.html.erb +0 -20
- data/app/components/better_ui/general/avatar/component.rb +0 -301
- data/app/components/better_ui/general/badge/component.html.erb +0 -23
- data/app/components/better_ui/general/badge/component.rb +0 -248
- data/app/components/better_ui/general/breadcrumb/component.html.erb +0 -15
- data/app/components/better_ui/general/breadcrumb/component.rb +0 -187
- data/app/components/better_ui/general/button/component.html.erb +0 -34
- data/app/components/better_ui/general/button/component.rb +0 -214
- data/app/components/better_ui/general/divider/component.html.erb +0 -10
- data/app/components/better_ui/general/divider/component.rb +0 -226
- data/app/components/better_ui/general/dropdown/component.html.erb +0 -25
- data/app/components/better_ui/general/dropdown/component.rb +0 -170
- data/app/components/better_ui/general/dropdown/divider_component.html.erb +0 -1
- data/app/components/better_ui/general/dropdown/divider_component.rb +0 -41
- data/app/components/better_ui/general/dropdown/item_component.html.erb +0 -6
- data/app/components/better_ui/general/dropdown/item_component.rb +0 -119
- data/app/components/better_ui/general/field/component.html.erb +0 -27
- data/app/components/better_ui/general/field/component.rb +0 -37
- data/app/components/better_ui/general/heading/component.html.erb +0 -22
- data/app/components/better_ui/general/heading/component.rb +0 -257
- data/app/components/better_ui/general/icon/component.html.erb +0 -7
- data/app/components/better_ui/general/icon/component.rb +0 -239
- data/app/components/better_ui/general/input/checkbox/component.html.erb +0 -5
- data/app/components/better_ui/general/input/checkbox/component.rb +0 -238
- data/app/components/better_ui/general/input/datetime/component.html.erb +0 -5
- data/app/components/better_ui/general/input/datetime/component.rb +0 -223
- data/app/components/better_ui/general/input/radio/component.html.erb +0 -5
- data/app/components/better_ui/general/input/radio/component.rb +0 -230
- data/app/components/better_ui/general/input/select/component.html.erb +0 -16
- data/app/components/better_ui/general/input/select/component.rb +0 -184
- data/app/components/better_ui/general/input/select/select_component.html.erb +0 -5
- data/app/components/better_ui/general/input/select/select_component.rb +0 -37
- data/app/components/better_ui/general/input/text/component.html.erb +0 -5
- data/app/components/better_ui/general/input/text/component.rb +0 -171
- data/app/components/better_ui/general/input/textarea/component.html.erb +0 -5
- data/app/components/better_ui/general/input/textarea/component.rb +0 -166
- data/app/components/better_ui/general/link/component.html.erb +0 -18
- data/app/components/better_ui/general/link/component.rb +0 -258
- data/app/components/better_ui/general/modal/component.html.erb +0 -5
- data/app/components/better_ui/general/modal/component.rb +0 -47
- data/app/components/better_ui/general/modal/modal_component.html.erb +0 -52
- data/app/components/better_ui/general/modal/modal_component.rb +0 -160
- data/app/components/better_ui/general/pagination/component.html.erb +0 -85
- data/app/components/better_ui/general/pagination/component.rb +0 -216
- data/app/components/better_ui/general/panel/component.html.erb +0 -28
- data/app/components/better_ui/general/panel/component.rb +0 -249
- data/app/components/better_ui/general/progress/component.html.erb +0 -11
- data/app/components/better_ui/general/progress/component.rb +0 -160
- data/app/components/better_ui/general/spinner/component.html.erb +0 -35
- data/app/components/better_ui/general/spinner/component.rb +0 -93
- data/app/components/better_ui/general/table/component.html.erb +0 -5
- data/app/components/better_ui/general/table/component.rb +0 -217
- data/app/components/better_ui/general/table/tbody_component.html.erb +0 -3
- data/app/components/better_ui/general/table/tbody_component.rb +0 -30
- data/app/components/better_ui/general/table/td_component.html.erb +0 -3
- data/app/components/better_ui/general/table/td_component.rb +0 -44
- data/app/components/better_ui/general/table/tfoot_component.html.erb +0 -3
- data/app/components/better_ui/general/table/tfoot_component.rb +0 -28
- data/app/components/better_ui/general/table/th_component.html.erb +0 -6
- data/app/components/better_ui/general/table/th_component.rb +0 -51
- data/app/components/better_ui/general/table/thead_component.html.erb +0 -3
- data/app/components/better_ui/general/table/thead_component.rb +0 -28
- data/app/components/better_ui/general/table/tr_component.html.erb +0 -3
- data/app/components/better_ui/general/table/tr_component.rb +0 -30
- data/app/components/better_ui/general/tabs/component.html.erb +0 -11
- data/app/components/better_ui/general/tabs/component.rb +0 -120
- data/app/components/better_ui/general/tabs/panel_component.html.erb +0 -3
- data/app/components/better_ui/general/tabs/panel_component.rb +0 -37
- data/app/components/better_ui/general/tabs/tab_component.html.erb +0 -13
- data/app/components/better_ui/general/tabs/tab_component.rb +0 -111
- data/app/components/better_ui/general/tag/component.html.erb +0 -3
- data/app/components/better_ui/general/tag/component.rb +0 -104
- data/app/components/better_ui/general/tooltip/component.html.erb +0 -7
- data/app/components/better_ui/general/tooltip/component.rb +0 -239
- data/app/helpers/better_ui/application/components/card/card_helper.rb +0 -96
- data/app/helpers/better_ui/application/components/card.rb +0 -11
- data/app/helpers/better_ui/application/components/main/main_helper.rb +0 -64
- data/app/helpers/better_ui/application/components/navbar/navbar_helper.rb +0 -77
- data/app/helpers/better_ui/application/components/sidebar/sidebar_helper.rb +0 -51
- data/app/helpers/better_ui/general/components/accordion/accordion_helper.rb +0 -73
- data/app/helpers/better_ui/general/components/accordion.rb +0 -11
- data/app/helpers/better_ui/general/components/alert/alert_helper.rb +0 -57
- data/app/helpers/better_ui/general/components/avatar/avatar_helper.rb +0 -29
- data/app/helpers/better_ui/general/components/badge/badge_helper.rb +0 -53
- data/app/helpers/better_ui/general/components/breadcrumb/breadcrumb_helper.rb +0 -37
- data/app/helpers/better_ui/general/components/button/button_helper.rb +0 -65
- data/app/helpers/better_ui/general/components/container/container_helper.rb +0 -60
- data/app/helpers/better_ui/general/components/divider/divider_helper.rb +0 -63
- data/app/helpers/better_ui/general/components/dropdown/divider_helper.rb +0 -32
- data/app/helpers/better_ui/general/components/dropdown/dropdown_helper.rb +0 -79
- data/app/helpers/better_ui/general/components/dropdown/item_helper.rb +0 -62
- data/app/helpers/better_ui/general/components/field/field_helper.rb +0 -26
- data/app/helpers/better_ui/general/components/heading/heading_helper.rb +0 -72
- data/app/helpers/better_ui/general/components/icon/icon_helper.rb +0 -16
- data/app/helpers/better_ui/general/components/input/checkbox/checkbox_helper.rb +0 -81
- data/app/helpers/better_ui/general/components/input/datetime/datetime_helper.rb +0 -91
- data/app/helpers/better_ui/general/components/input/radio/radio_helper.rb +0 -79
- data/app/helpers/better_ui/general/components/input/radio_group/radio_group_helper.rb +0 -124
- data/app/helpers/better_ui/general/components/input/select/select_helper.rb +0 -70
- data/app/helpers/better_ui/general/components/input/text/text_helper.rb +0 -138
- data/app/helpers/better_ui/general/components/input/textarea/textarea_helper.rb +0 -73
- data/app/helpers/better_ui/general/components/link/link_helper.rb +0 -89
- data/app/helpers/better_ui/general/components/modal/modal_helper.rb +0 -85
- data/app/helpers/better_ui/general/components/modal.rb +0 -11
- data/app/helpers/better_ui/general/components/pagination/pagination_helper.rb +0 -82
- data/app/helpers/better_ui/general/components/panel/panel_helper.rb +0 -83
- data/app/helpers/better_ui/general/components/progress/progress_helper.rb +0 -53
- data/app/helpers/better_ui/general/components/spinner/spinner_helper.rb +0 -19
- data/app/helpers/better_ui/general/components/table/table_helper.rb +0 -53
- data/app/helpers/better_ui/general/components/table/tbody_helper.rb +0 -13
- data/app/helpers/better_ui/general/components/table/td_helper.rb +0 -19
- data/app/helpers/better_ui/general/components/table/tfoot_helper.rb +0 -13
- data/app/helpers/better_ui/general/components/table/th_helper.rb +0 -19
- data/app/helpers/better_ui/general/components/table/thead_helper.rb +0 -13
- data/app/helpers/better_ui/general/components/table/tr_helper.rb +0 -13
- data/app/helpers/better_ui/general/components/tabs/panel_helper.rb +0 -62
- data/app/helpers/better_ui/general/components/tabs/tab_helper.rb +0 -55
- data/app/helpers/better_ui/general/components/tabs/tabs_helper.rb +0 -95
- data/app/helpers/better_ui/general/components/tag/tag_helper.rb +0 -26
- data/app/helpers/better_ui/general/components/tooltip/tooltip_helper.rb +0 -60
- data/app/jobs/better_ui/application_job.rb +0 -4
- data/app/mailers/better_ui/application_mailer.rb +0 -6
- data/config/initializers/lookbook.rb +0 -23
- data/lib/better_ui/railtie.rb +0 -20
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterUi
|
|
4
|
+
module Forms
|
|
5
|
+
# A checkbox input component with support for labels, hints, errors, and color variants.
|
|
6
|
+
#
|
|
7
|
+
# This component provides a styled checkbox with customizable colors, sizes, and label positioning.
|
|
8
|
+
# Unlike text inputs, the label appears inline (left or right) with the checkbox rather than above.
|
|
9
|
+
#
|
|
10
|
+
# @example Basic checkbox
|
|
11
|
+
# <%= render BetterUi::Forms::CheckboxComponent.new(
|
|
12
|
+
# name: "user[terms]",
|
|
13
|
+
# label: "I agree to the terms and conditions"
|
|
14
|
+
# ) %>
|
|
15
|
+
#
|
|
16
|
+
# @example Checkbox with different variant
|
|
17
|
+
# <%= render BetterUi::Forms::CheckboxComponent.new(
|
|
18
|
+
# name: "settings[notifications]",
|
|
19
|
+
# label: "Enable notifications",
|
|
20
|
+
# variant: :success,
|
|
21
|
+
# checked: true
|
|
22
|
+
# ) %>
|
|
23
|
+
#
|
|
24
|
+
# @example Checkbox with label on left
|
|
25
|
+
# <%= render BetterUi::Forms::CheckboxComponent.new(
|
|
26
|
+
# name: "user[active]",
|
|
27
|
+
# label: "Active",
|
|
28
|
+
# label_position: :left
|
|
29
|
+
# ) %>
|
|
30
|
+
#
|
|
31
|
+
# @example With validation errors
|
|
32
|
+
# <%= render BetterUi::Forms::CheckboxComponent.new(
|
|
33
|
+
# name: "user[terms]",
|
|
34
|
+
# label: "I agree to the terms",
|
|
35
|
+
# errors: ["You must agree to the terms"]
|
|
36
|
+
# ) %>
|
|
37
|
+
#
|
|
38
|
+
# @example Using with Rails form builder
|
|
39
|
+
# <%= form_with model: @user, builder: BetterUi::UiFormBuilder do |f| %>
|
|
40
|
+
# <%= f.bui_checkbox :newsletter, label: "Subscribe to newsletter" %>
|
|
41
|
+
# <% end %>
|
|
42
|
+
#
|
|
43
|
+
# @see BetterUi::UiFormBuilder#bui_checkbox
|
|
44
|
+
class CheckboxComponent < ApplicationComponent
|
|
45
|
+
# Available size variants for checkbox inputs.
|
|
46
|
+
# Each size adjusts the checkbox dimensions and spacing proportionally.
|
|
47
|
+
#
|
|
48
|
+
# @return [Array<Symbol>] the list of valid size options (:xs, :sm, :md, :lg, :xl)
|
|
49
|
+
SIZES = %i[xs sm md lg xl].freeze
|
|
50
|
+
|
|
51
|
+
# Available label positions relative to the checkbox.
|
|
52
|
+
#
|
|
53
|
+
# @return [Array<Symbol>] the list of valid label positions (:left, :right)
|
|
54
|
+
LABEL_POSITIONS = %i[left right].freeze
|
|
55
|
+
|
|
56
|
+
# Initializes a new checkbox component.
|
|
57
|
+
#
|
|
58
|
+
# @param name [String] the name attribute for the checkbox (required for form submission)
|
|
59
|
+
# @param value [String] the value submitted when checkbox is checked (defaults to "1")
|
|
60
|
+
# @param checked [Boolean] whether the checkbox is initially checked
|
|
61
|
+
# @param label [String, nil] the label text displayed next to the checkbox
|
|
62
|
+
# @param hint [String, nil] helpful hint text displayed below the checkbox
|
|
63
|
+
# @param variant [Symbol] the color variant (:primary, :secondary, :accent, :success, :danger, :warning, :info, :light, :dark)
|
|
64
|
+
# @param size [Symbol] the size variant (:xs, :sm, :md, :lg, :xl), defaults to :md
|
|
65
|
+
# @param label_position [Symbol] where to position the label (:left, :right), defaults to :right
|
|
66
|
+
# @param disabled [Boolean] whether the checkbox should be disabled (non-interactive), defaults to false
|
|
67
|
+
# @param readonly [Boolean] whether the checkbox should be readonly (viewable but not editable), defaults to false
|
|
68
|
+
# @param required [Boolean] whether the field is required (shows asterisk indicator), defaults to false
|
|
69
|
+
# @param errors [Array<String>, String, nil] validation error messages to display below the checkbox
|
|
70
|
+
# @param container_classes [String, Array<String>, nil] additional CSS classes for the outer wrapper
|
|
71
|
+
# @param label_classes [String, Array<String>, nil] additional CSS classes for the label element
|
|
72
|
+
# @param checkbox_classes [String, Array<String>, nil] additional CSS classes for the checkbox element
|
|
73
|
+
# @param hint_classes [String, Array<String>, nil] additional CSS classes for the hint text
|
|
74
|
+
# @param error_classes [String, Array<String>, nil] additional CSS classes for error messages
|
|
75
|
+
# @param options [Hash] additional HTML attributes to pass through to the checkbox element
|
|
76
|
+
#
|
|
77
|
+
# @raise [ArgumentError] if variant, size, or label_position is invalid
|
|
78
|
+
def initialize(
|
|
79
|
+
name:,
|
|
80
|
+
value: "1",
|
|
81
|
+
checked: false,
|
|
82
|
+
label: nil,
|
|
83
|
+
hint: nil,
|
|
84
|
+
variant: :primary,
|
|
85
|
+
size: :md,
|
|
86
|
+
label_position: :right,
|
|
87
|
+
disabled: false,
|
|
88
|
+
readonly: false,
|
|
89
|
+
required: false,
|
|
90
|
+
errors: nil,
|
|
91
|
+
container_classes: nil,
|
|
92
|
+
label_classes: nil,
|
|
93
|
+
checkbox_classes: nil,
|
|
94
|
+
hint_classes: nil,
|
|
95
|
+
error_classes: nil,
|
|
96
|
+
**options
|
|
97
|
+
)
|
|
98
|
+
@name = name
|
|
99
|
+
@value = value
|
|
100
|
+
@checked = checked
|
|
101
|
+
@label = label
|
|
102
|
+
@hint = hint
|
|
103
|
+
@variant = validate_variant(variant)
|
|
104
|
+
@size = validate_size(size)
|
|
105
|
+
@label_position = validate_label_position(label_position)
|
|
106
|
+
@disabled = disabled
|
|
107
|
+
@readonly = readonly
|
|
108
|
+
@required = required
|
|
109
|
+
@errors = Array(errors).compact.reject(&:blank?)
|
|
110
|
+
@container_classes = container_classes
|
|
111
|
+
@label_classes = label_classes
|
|
112
|
+
@checkbox_classes = checkbox_classes
|
|
113
|
+
@hint_classes = hint_classes
|
|
114
|
+
@error_classes = error_classes
|
|
115
|
+
@options = options
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
# Validates that the provided variant is one of the allowed VARIANTS.
|
|
121
|
+
#
|
|
122
|
+
# @param variant [Symbol] the variant to validate
|
|
123
|
+
# @return [Symbol] the validated variant
|
|
124
|
+
# @raise [ArgumentError] if variant is not in VARIANTS
|
|
125
|
+
# @api private
|
|
126
|
+
def validate_variant(variant)
|
|
127
|
+
unless BetterUi::ApplicationComponent::VARIANTS.key?(variant)
|
|
128
|
+
raise ArgumentError, "Invalid variant: #{variant}. Must be one of: #{BetterUi::ApplicationComponent::VARIANTS.keys.join(', ')}"
|
|
129
|
+
end
|
|
130
|
+
variant
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Validates that the provided size is one of the allowed SIZES.
|
|
134
|
+
#
|
|
135
|
+
# @param size [Symbol] the size to validate
|
|
136
|
+
# @return [Symbol] the validated size
|
|
137
|
+
# @raise [ArgumentError] if size is not in SIZES
|
|
138
|
+
# @api private
|
|
139
|
+
def validate_size(size)
|
|
140
|
+
unless SIZES.include?(size)
|
|
141
|
+
raise ArgumentError, "Invalid size: #{size}. Must be one of: #{SIZES.join(', ')}"
|
|
142
|
+
end
|
|
143
|
+
size
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Validates that the provided label_position is one of the allowed LABEL_POSITIONS.
|
|
147
|
+
#
|
|
148
|
+
# @param label_position [Symbol] the label_position to validate
|
|
149
|
+
# @return [Symbol] the validated label_position
|
|
150
|
+
# @raise [ArgumentError] if label_position is not in LABEL_POSITIONS
|
|
151
|
+
# @api private
|
|
152
|
+
def validate_label_position(label_position)
|
|
153
|
+
unless LABEL_POSITIONS.include?(label_position)
|
|
154
|
+
raise ArgumentError, "Invalid label_position: #{label_position}. Must be one of: #{LABEL_POSITIONS.join(', ')}"
|
|
155
|
+
end
|
|
156
|
+
label_position
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Checks if the component has any validation errors to display.
|
|
160
|
+
#
|
|
161
|
+
# @return [Boolean] true if errors are present, false otherwise
|
|
162
|
+
# @api private
|
|
163
|
+
def has_errors?
|
|
164
|
+
@errors.present?
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Returns the CSS classes for the outermost wrapper element.
|
|
168
|
+
#
|
|
169
|
+
# @return [String] the merged CSS class string
|
|
170
|
+
# @api private
|
|
171
|
+
def wrapper_classes
|
|
172
|
+
css_classes([
|
|
173
|
+
"form-field-wrapper",
|
|
174
|
+
@container_classes
|
|
175
|
+
].flatten.compact)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Returns the CSS classes for the checkbox and label wrapper.
|
|
179
|
+
#
|
|
180
|
+
# Handles flex direction based on label_position.
|
|
181
|
+
#
|
|
182
|
+
# @return [String] the merged CSS class string
|
|
183
|
+
# @api private
|
|
184
|
+
def checkbox_wrapper_classes
|
|
185
|
+
css_classes([
|
|
186
|
+
"flex",
|
|
187
|
+
"items-start",
|
|
188
|
+
@label_position == :left ? "flex-row-reverse" : "flex-row",
|
|
189
|
+
gap_classes
|
|
190
|
+
].flatten.compact)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Returns gap classes based on component size.
|
|
194
|
+
#
|
|
195
|
+
# @return [String] the gap class for the current component size
|
|
196
|
+
# @api private
|
|
197
|
+
def gap_classes
|
|
198
|
+
case @size
|
|
199
|
+
when :xs then "gap-1.5"
|
|
200
|
+
when :sm then "gap-2"
|
|
201
|
+
when :md then "gap-2.5"
|
|
202
|
+
when :lg then "gap-3"
|
|
203
|
+
when :xl then "gap-3.5"
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Returns the CSS classes for the checkbox element itself.
|
|
208
|
+
#
|
|
209
|
+
# @return [String] the merged CSS class string for the checkbox element
|
|
210
|
+
# @api private
|
|
211
|
+
def checkbox_element_classes
|
|
212
|
+
css_classes([
|
|
213
|
+
base_checkbox_classes,
|
|
214
|
+
size_checkbox_classes,
|
|
215
|
+
variant_checkbox_classes,
|
|
216
|
+
state_checkbox_classes,
|
|
217
|
+
@checkbox_classes
|
|
218
|
+
].flatten.compact)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Returns the base CSS classes common to all checkboxes.
|
|
222
|
+
#
|
|
223
|
+
# @return [Array<String>] array of base CSS class strings
|
|
224
|
+
# @api private
|
|
225
|
+
def base_checkbox_classes
|
|
226
|
+
[
|
|
227
|
+
"appearance-none",
|
|
228
|
+
"shrink-0",
|
|
229
|
+
"rounded",
|
|
230
|
+
"border",
|
|
231
|
+
"cursor-pointer",
|
|
232
|
+
"transition-colors",
|
|
233
|
+
"duration-200",
|
|
234
|
+
"focus:outline-none",
|
|
235
|
+
"focus:ring-2",
|
|
236
|
+
"focus:ring-offset-2"
|
|
237
|
+
]
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Returns size-specific CSS classes for the checkbox.
|
|
241
|
+
#
|
|
242
|
+
# @return [Array<String>] array of size-specific CSS class strings
|
|
243
|
+
# @api private
|
|
244
|
+
def size_checkbox_classes
|
|
245
|
+
case @size
|
|
246
|
+
when :xs then [ "w-3", "h-3" ]
|
|
247
|
+
when :sm then [ "w-4", "h-4" ]
|
|
248
|
+
when :md then [ "w-5", "h-5" ]
|
|
249
|
+
when :lg then [ "w-6", "h-6" ]
|
|
250
|
+
when :xl then [ "w-7", "h-7" ]
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Returns variant-specific CSS classes for the checkbox.
|
|
255
|
+
# Uses literal strings for Tailwind JIT detection.
|
|
256
|
+
#
|
|
257
|
+
# @return [Array<String>] array of variant-specific CSS class strings
|
|
258
|
+
# @api private
|
|
259
|
+
def variant_checkbox_classes
|
|
260
|
+
case @variant
|
|
261
|
+
when :primary
|
|
262
|
+
[ "border-gray-300", "checked:bg-primary-600", "checked:border-primary-600", "focus:ring-primary-500" ]
|
|
263
|
+
when :secondary
|
|
264
|
+
[ "border-gray-300", "checked:bg-secondary-600", "checked:border-secondary-600", "focus:ring-secondary-500" ]
|
|
265
|
+
when :accent
|
|
266
|
+
[ "border-gray-300", "checked:bg-accent-600", "checked:border-accent-600", "focus:ring-accent-500" ]
|
|
267
|
+
when :success
|
|
268
|
+
[ "border-gray-300", "checked:bg-success-600", "checked:border-success-600", "focus:ring-success-500" ]
|
|
269
|
+
when :danger
|
|
270
|
+
[ "border-gray-300", "checked:bg-danger-600", "checked:border-danger-600", "focus:ring-danger-500" ]
|
|
271
|
+
when :warning
|
|
272
|
+
[ "border-gray-300", "checked:bg-warning-600", "checked:border-warning-600", "focus:ring-warning-500" ]
|
|
273
|
+
when :info
|
|
274
|
+
[ "border-gray-300", "checked:bg-info-600", "checked:border-info-600", "focus:ring-info-500" ]
|
|
275
|
+
when :light
|
|
276
|
+
[ "border-gray-300", "checked:bg-gray-200", "checked:border-gray-400", "focus:ring-gray-400", "checked-dark" ]
|
|
277
|
+
when :dark
|
|
278
|
+
[ "border-gray-400", "checked:bg-gray-800", "checked:border-gray-800", "focus:ring-gray-600" ]
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Returns state-specific CSS classes for the checkbox.
|
|
283
|
+
#
|
|
284
|
+
# @return [Array<String>] array of state-specific CSS class strings
|
|
285
|
+
# @api private
|
|
286
|
+
def state_checkbox_classes
|
|
287
|
+
if @disabled
|
|
288
|
+
[ "bg-gray-100", "cursor-not-allowed", "opacity-60" ]
|
|
289
|
+
elsif @readonly
|
|
290
|
+
[ "bg-gray-50", "cursor-default", "pointer-events-none" ]
|
|
291
|
+
elsif has_errors?
|
|
292
|
+
[ "bg-white", "border-danger-500", "focus:ring-danger-500" ]
|
|
293
|
+
else
|
|
294
|
+
[ "bg-white" ]
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Returns the CSS classes for the label element.
|
|
299
|
+
#
|
|
300
|
+
# @return [String] the merged CSS class string for the label
|
|
301
|
+
# @api private
|
|
302
|
+
def label_element_classes
|
|
303
|
+
css_classes([
|
|
304
|
+
"select-none",
|
|
305
|
+
"cursor-pointer",
|
|
306
|
+
@disabled ? "text-gray-400 cursor-not-allowed" : "text-gray-700",
|
|
307
|
+
label_size_classes,
|
|
308
|
+
@label_classes
|
|
309
|
+
].flatten.compact)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Returns size-specific CSS classes for the label text.
|
|
313
|
+
#
|
|
314
|
+
# @return [String] the text size class for the current component size
|
|
315
|
+
# @api private
|
|
316
|
+
def label_size_classes
|
|
317
|
+
case @size
|
|
318
|
+
when :xs then "text-xs"
|
|
319
|
+
when :sm then "text-sm"
|
|
320
|
+
when :md then "text-sm"
|
|
321
|
+
when :lg then "text-base"
|
|
322
|
+
when :xl then "text-lg"
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Returns the CSS classes for the hint text element.
|
|
327
|
+
#
|
|
328
|
+
# @return [String] the merged CSS class string for the hint text
|
|
329
|
+
# @api private
|
|
330
|
+
def hint_element_classes
|
|
331
|
+
css_classes([
|
|
332
|
+
"block",
|
|
333
|
+
"text-gray-600",
|
|
334
|
+
"mt-1",
|
|
335
|
+
hint_size_classes,
|
|
336
|
+
@hint_classes
|
|
337
|
+
].flatten.compact)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Returns size-specific CSS classes for the hint text.
|
|
341
|
+
#
|
|
342
|
+
# @return [String] the text size class for the current component size
|
|
343
|
+
# @api private
|
|
344
|
+
def hint_size_classes
|
|
345
|
+
case @size
|
|
346
|
+
when :xs then "text-xs"
|
|
347
|
+
when :sm then "text-xs"
|
|
348
|
+
when :md then "text-sm"
|
|
349
|
+
when :lg then "text-sm"
|
|
350
|
+
when :xl then "text-base"
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Returns the CSS classes for the error messages container.
|
|
355
|
+
#
|
|
356
|
+
# @return [String] the merged CSS class string for the error messages container
|
|
357
|
+
# @api private
|
|
358
|
+
def errors_element_classes
|
|
359
|
+
css_classes([
|
|
360
|
+
"text-danger-600",
|
|
361
|
+
"mt-1",
|
|
362
|
+
"space-y-0.5",
|
|
363
|
+
error_size_classes,
|
|
364
|
+
@error_classes
|
|
365
|
+
].flatten.compact)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# Returns size-specific CSS classes for error message text.
|
|
369
|
+
#
|
|
370
|
+
# @return [String] the text size class for the current component size
|
|
371
|
+
# @api private
|
|
372
|
+
def error_size_classes
|
|
373
|
+
case @size
|
|
374
|
+
when :xs then "text-xs"
|
|
375
|
+
when :sm then "text-xs"
|
|
376
|
+
when :md then "text-sm"
|
|
377
|
+
when :lg then "text-sm"
|
|
378
|
+
when :xl then "text-base"
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# Returns the complete set of HTML attributes for the checkbox element.
|
|
383
|
+
#
|
|
384
|
+
# @return [Hash] hash of HTML attributes for the checkbox element
|
|
385
|
+
# @api private
|
|
386
|
+
def checkbox_attributes
|
|
387
|
+
attrs = {
|
|
388
|
+
type: "checkbox",
|
|
389
|
+
id: input_id,
|
|
390
|
+
name: @name,
|
|
391
|
+
value: @value,
|
|
392
|
+
checked: @checked || nil,
|
|
393
|
+
disabled: @disabled || nil,
|
|
394
|
+
readonly: @readonly || nil,
|
|
395
|
+
required: @required || nil,
|
|
396
|
+
class: checkbox_element_classes,
|
|
397
|
+
**@options
|
|
398
|
+
}.compact
|
|
399
|
+
|
|
400
|
+
# Handle readonly by adding aria attribute since checkboxes don't support readonly HTML attribute
|
|
401
|
+
if @readonly
|
|
402
|
+
attrs[:aria] ||= {}
|
|
403
|
+
attrs[:aria][:readonly] = "true"
|
|
404
|
+
attrs.delete(:readonly)
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
attrs
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Returns the input ID, either from options or generated from name.
|
|
411
|
+
#
|
|
412
|
+
# @return [String] the input ID
|
|
413
|
+
# @api private
|
|
414
|
+
def input_id
|
|
415
|
+
@options[:id] || @name.to_s.gsub(/\[|\]/, "_").gsub(/_+/, "_").chomp("_")
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
end
|
data/app/components/better_ui/forms/checkbox_group_component/checkbox_group_component.html.erb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<fieldset <%= tag.attributes(fieldset_attributes) %>>
|
|
2
|
+
<% if @legend.present? %>
|
|
3
|
+
<legend class="<%= legend_element_classes %>">
|
|
4
|
+
<%= @legend %>
|
|
5
|
+
<% if @required %>
|
|
6
|
+
<span class="text-danger-600 ml-0.5">*</span>
|
|
7
|
+
<% end %>
|
|
8
|
+
</legend>
|
|
9
|
+
<% end %>
|
|
10
|
+
|
|
11
|
+
<div class="<%= items_wrapper_classes %>">
|
|
12
|
+
<% @collection.each_with_index do |item, index| %>
|
|
13
|
+
<%= render BetterUi::Forms::CheckboxComponent.new(
|
|
14
|
+
name: field_name,
|
|
15
|
+
value: item_value(item),
|
|
16
|
+
checked: item_checked?(item_value(item)),
|
|
17
|
+
label: item_label(item),
|
|
18
|
+
variant: @variant,
|
|
19
|
+
size: @size,
|
|
20
|
+
disabled: @disabled,
|
|
21
|
+
id: item_id(index),
|
|
22
|
+
errors: nil
|
|
23
|
+
) %>
|
|
24
|
+
<% end %>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<% if @hint.present? %>
|
|
28
|
+
<div class="<%= hint_element_classes %>">
|
|
29
|
+
<%= @hint %>
|
|
30
|
+
</div>
|
|
31
|
+
<% end %>
|
|
32
|
+
|
|
33
|
+
<% if has_errors? %>
|
|
34
|
+
<div class="<%= errors_element_classes %>">
|
|
35
|
+
<% @errors.each do |error| %>
|
|
36
|
+
<div><%= error %></div>
|
|
37
|
+
<% end %>
|
|
38
|
+
</div>
|
|
39
|
+
<% end %>
|
|
40
|
+
</fieldset>
|