fluxbit_view_components 0.4.3 โ†’ 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98187d5ac8174cfe0b38638e43b70e8076ec32068cab920e5829105e0e903f6a
4
- data.tar.gz: 1e7c9b50861f506bb56407ecdd008988a96c9b8d3f79f75e43fbae7f2642c989
3
+ metadata.gz: 6f66b9f23ca4ef3e2fe559a212875eda72091ec0e44473efdeade01655b01ddf
4
+ data.tar.gz: 5d17abad070b64489453dde8cc700f3c464d4b97d60f0cb8aab4aa0137013385
5
5
  SHA512:
6
- metadata.gz: 426024fbf92e4dd34587a9f14057a541e5f00cb501578026c940ffcc98739398cded786b733c36aa0d1d4de2292ce56b7700e9b93d630c4b15a9e58f9098a1e6
7
- data.tar.gz: 688a8bec19c6def98088f4e59dc8bd96f46c7fc7818612e799bb77f2f44ebced4dc2055d77b7afcad78b6bbd1b5e0d9c16859fcf4ba62866ff472a15955f4c63
6
+ metadata.gz: 0f893f775299b24f11ebc2c8120099c13999f525502d314d9ad87709a02fbbf6dd194bede20193e0cf5a5a7bd53b2b9e58d1298113df77f3ee7b958988c81bfd
7
+ data.tar.gz: 802ffce4186aa7fe55a32c56b904e084b4c346b6e50a5891bab9683eaa636cde5654b97e0c9b4502e30ad20e4d21b4bdcb2e68260cd66fd9d1aad72a7fd1ea73
@@ -8,6 +8,7 @@ import FxProgress from './progress_controller'
8
8
  import FxRowClick from './row_click_controller'
9
9
  import FxSelectAll from './select_all_controller'
10
10
  import FxSpinnerPercent from './spinner_percent_controller'
11
+ import FxTelephone from './telephone_controller'
11
12
  import FxThemeButton from './theme_button_controller'
12
13
 
13
14
  export {
@@ -21,6 +22,7 @@ export {
21
22
  FxRowClick,
22
23
  FxSelectAll,
23
24
  FxSpinnerPercent,
25
+ FxTelephone,
24
26
  FxThemeButton
25
27
  }
26
28
 
@@ -35,6 +37,7 @@ export function registerFluxbitControllers(application) {
35
37
  application.register('fx-row-click', FxRowClick)
36
38
  application.register('fx-select-all', FxSelectAll)
37
39
  application.register('fx-spinner-percent', FxSpinnerPercent)
40
+ application.register('fx-telephone', FxTelephone)
38
41
  application.register('fx-theme-button', FxThemeButton)
39
42
 
40
43
  // Make controllers globally accessible for vanilla JS
@@ -50,6 +53,7 @@ export function registerFluxbitControllers(application) {
50
53
  FxRowClick,
51
54
  FxSelectAll,
52
55
  FxSpinnerPercent,
56
+ FxTelephone,
53
57
  FxThemeButton
54
58
  }
55
59
  }
@@ -0,0 +1,98 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static targets = ["countrySelect"]
5
+ static values = {
6
+ mask: { type: String, default: "(##) #####-####" }
7
+ }
8
+
9
+ connect() {
10
+ this.input = this.element.querySelector('input[type="tel"]')
11
+ if (this.input) {
12
+ this.input.addEventListener('input', this.applyMask.bind(this))
13
+ this.input.addEventListener('keydown', this.handleBackspace.bind(this))
14
+
15
+ // Apply mask to existing value on load
16
+ if (this.input.value) {
17
+ this.applyMask({ target: this.input })
18
+ }
19
+ }
20
+ }
21
+
22
+ disconnect() {
23
+ if (this.input) {
24
+ this.input.removeEventListener('input', this.applyMask.bind(this))
25
+ this.input.removeEventListener('keydown', this.handleBackspace.bind(this))
26
+ }
27
+ }
28
+
29
+ updateMask(event) {
30
+ const selectedOption = event.target.options[event.target.selectedIndex]
31
+ const newMask = selectedOption.dataset.mask
32
+
33
+ if (newMask) {
34
+ this.maskValue = newMask
35
+ // Clear the current value and reapply mask
36
+ const currentValue = this.input.value
37
+ this.input.value = ''
38
+ this.input.value = this.getCleanValue(currentValue)
39
+ this.applyMask({ target: this.input })
40
+ }
41
+ }
42
+
43
+ handleBackspace(event) {
44
+ if (event.key === 'Backspace' || event.keyCode === 8) {
45
+ const cursorPosition = this.input.selectionStart
46
+ const value = this.input.value
47
+
48
+ // Check if the character before cursor is a mask character
49
+ if (cursorPosition > 0) {
50
+ const charBefore = value.charAt(cursorPosition - 1)
51
+ if (this.isMaskCharacter(charBefore)) {
52
+ event.preventDefault()
53
+ // Find the previous digit and remove it
54
+ let newPosition = cursorPosition - 1
55
+ while (newPosition > 0 && this.isMaskCharacter(value.charAt(newPosition))) {
56
+ newPosition--
57
+ }
58
+
59
+ if (newPosition >= 0) {
60
+ const cleanValue = this.getCleanValue(value.substring(0, newPosition) + value.substring(cursorPosition))
61
+ this.input.value = cleanValue
62
+ this.applyMask({ target: this.input })
63
+ this.input.setSelectionRange(newPosition, newPosition)
64
+ }
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ applyMask(event) {
71
+ const input = event.target
72
+ let value = this.getCleanValue(input.value)
73
+ let maskedValue = ''
74
+ let valueIndex = 0
75
+
76
+ for (let i = 0; i < this.maskValue.length && valueIndex < value.length; i++) {
77
+ const maskChar = this.maskValue.charAt(i)
78
+
79
+ if (maskChar === '#') {
80
+ maskedValue += value.charAt(valueIndex)
81
+ valueIndex++
82
+ } else {
83
+ maskedValue += maskChar
84
+ }
85
+ }
86
+
87
+ input.value = maskedValue
88
+ }
89
+
90
+ getCleanValue(value) {
91
+ // Remove all non-numeric characters
92
+ return value.replace(/\D/g, '')
93
+ }
94
+
95
+ isMaskCharacter(char) {
96
+ return /[\s\-\(\)\/\.]/.test(char)
97
+ }
98
+ }
@@ -1067,6 +1067,93 @@ class FxSpinnerPercent extends Controller {
1067
1067
  }
1068
1068
  }
1069
1069
 
1070
+ class FxTelephone extends Controller {
1071
+ static targets=[ "countrySelect" ];
1072
+ static values={
1073
+ mask: {
1074
+ type: String,
1075
+ default: "(##) #####-####"
1076
+ }
1077
+ };
1078
+ connect() {
1079
+ this.input = this.element.querySelector('input[type="tel"]');
1080
+ if (this.input) {
1081
+ this.input.addEventListener("input", this.applyMask.bind(this));
1082
+ this.input.addEventListener("keydown", this.handleBackspace.bind(this));
1083
+ if (this.input.value) {
1084
+ this.applyMask({
1085
+ target: this.input
1086
+ });
1087
+ }
1088
+ }
1089
+ }
1090
+ disconnect() {
1091
+ if (this.input) {
1092
+ this.input.removeEventListener("input", this.applyMask.bind(this));
1093
+ this.input.removeEventListener("keydown", this.handleBackspace.bind(this));
1094
+ }
1095
+ }
1096
+ updateMask(event) {
1097
+ const selectedOption = event.target.options[event.target.selectedIndex];
1098
+ const newMask = selectedOption.dataset.mask;
1099
+ if (newMask) {
1100
+ this.maskValue = newMask;
1101
+ const currentValue = this.input.value;
1102
+ this.input.value = "";
1103
+ this.input.value = this.getCleanValue(currentValue);
1104
+ this.applyMask({
1105
+ target: this.input
1106
+ });
1107
+ }
1108
+ }
1109
+ handleBackspace(event) {
1110
+ if (event.key === "Backspace" || event.keyCode === 8) {
1111
+ const cursorPosition = this.input.selectionStart;
1112
+ const value = this.input.value;
1113
+ if (cursorPosition > 0) {
1114
+ const charBefore = value.charAt(cursorPosition - 1);
1115
+ if (this.isMaskCharacter(charBefore)) {
1116
+ event.preventDefault();
1117
+ let newPosition = cursorPosition - 1;
1118
+ while (newPosition > 0 && this.isMaskCharacter(value.charAt(newPosition))) {
1119
+ newPosition--;
1120
+ }
1121
+ if (newPosition >= 0) {
1122
+ const cleanValue = this.getCleanValue(value.substring(0, newPosition) + value.substring(cursorPosition));
1123
+ this.input.value = cleanValue;
1124
+ this.applyMask({
1125
+ target: this.input
1126
+ });
1127
+ this.input.setSelectionRange(newPosition, newPosition);
1128
+ }
1129
+ }
1130
+ }
1131
+ }
1132
+ }
1133
+ applyMask(event) {
1134
+ const input = event.target;
1135
+ let value = this.getCleanValue(input.value);
1136
+ let maskedValue = "";
1137
+ let valueIndex = 0;
1138
+ for (let i = 0; i < this.maskValue.length && valueIndex < value.length; i++) {
1139
+ const maskChar = this.maskValue.charAt(i);
1140
+ if (maskChar === "#") {
1141
+ maskedValue += value.charAt(valueIndex);
1142
+ valueIndex++;
1143
+ } else {
1144
+ maskedValue += maskChar;
1145
+ }
1146
+ }
1147
+ input.value = maskedValue;
1148
+ }
1149
+ getCleanValue(value) {
1150
+ return value.replace(/\D/g, "");
1151
+ }
1152
+ isMaskCharacter(char) {
1153
+ return /[\s\-\(\)\/\.]/.test(char);
1154
+ }
1155
+ }
1156
+
1070
1157
  class FxThemeButton extends Controller {
1071
1158
  static targets=[ "lightIcon", "darkIcon", "systemIcon" ];
1072
1159
  static values={
@@ -1154,6 +1241,7 @@ function registerFluxbitControllers(application) {
1154
1241
  application.register("fx-row-click", FxRowClick);
1155
1242
  application.register("fx-select-all", FxSelectAll);
1156
1243
  application.register("fx-spinner-percent", FxSpinnerPercent);
1244
+ application.register("fx-telephone", FxTelephone);
1157
1245
  application.register("fx-theme-button", FxThemeButton);
1158
1246
  if (typeof window !== "undefined") {
1159
1247
  window.FluxbitControllers = {
@@ -1167,9 +1255,10 @@ function registerFluxbitControllers(application) {
1167
1255
  FxRowClick: FxRowClick,
1168
1256
  FxSelectAll: FxSelectAll,
1169
1257
  FxSpinnerPercent: FxSpinnerPercent,
1258
+ FxTelephone: FxTelephone,
1170
1259
  FxThemeButton: FxThemeButton
1171
1260
  };
1172
1261
  }
1173
1262
  }
1174
1263
 
1175
- export { FxAssigner, FxAutoSubmit, FxDrawer, FxMethodLink, FxModal, FxPassword, FxProgress, FxRowClick, FxSelectAll, FxSpinnerPercent, FxThemeButton, registerFluxbitControllers };
1264
+ export { FxAssigner, FxAutoSubmit, FxDrawer, FxMethodLink, FxModal, FxPassword, FxProgress, FxRowClick, FxSelectAll, FxSpinnerPercent, FxTelephone, FxThemeButton, registerFluxbitControllers };
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The `Fluxbit::Form::TelephoneComponent` is a telephone input component that extends `Fluxbit::Form::TextFieldComponent`.
4
+ # It provides a styled telephone input with an integrated country code selector showing country flags and dialing codes.
5
+ # The input includes automatic masking for phone numbers.
6
+ #
7
+ # @example Basic usage
8
+ # = render Fluxbit::Form::TelephoneComponent.new(name: :phone)
9
+ #
10
+ # @example With default country
11
+ # = render Fluxbit::Form::TelephoneComponent.new(name: :phone, default_country: "BR")
12
+ #
13
+ class Fluxbit::Form::TelephoneComponent < Fluxbit::Form::TextFieldComponent
14
+ include Fluxbit::Config::Form::TelephoneComponent
15
+
16
+ # Initializes the telephone component with the given properties.
17
+ #
18
+ # @param default_country [String] The default country code (ISO 3166-1 alpha-2, e.g., "BR", "US")
19
+ # @param country_field_name [String] Name for the hidden country code field (optional, deprecated in favor of :country)
20
+ # @param country [Symbol] Attribute name for the country field when using form builder (e.g., :phone_country)
21
+ # @param ... all other parameters from TextFieldComponent
22
+ def initialize(**props)
23
+ @default_country = props.delete(:default_country) || @@default_country
24
+ @country_field_name = props.delete(:country_field_name)
25
+ @country_attribute = props.delete(:country)
26
+
27
+ # Set default sizing from config if not specified
28
+ props[:sizing] = @@default_sizing unless props.key?(:sizing)
29
+
30
+ # Force type to tel
31
+ props[:type] = :tel
32
+
33
+ super(**props)
34
+
35
+ # Override the input classes to match our custom sizing
36
+ override_input_sizing
37
+ end
38
+
39
+ def call
40
+ content_tag :div, **@wrapper_html do
41
+ safe_join [
42
+ label,
43
+ telephone_input_container,
44
+ help_text
45
+ ]
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def override_input_sizing
52
+ # Remove the original size classes
53
+ current_classes = @props[:class].to_s
54
+
55
+ # Get size class from config
56
+ size_index = [@sizing, 0].max
57
+ size_index = [size_index, @@telephone_styles[:input][:sizes].length - 1].min
58
+ custom_size_class = @@telephone_styles[:input][:sizes][size_index]
59
+
60
+ # Remove the old size class and add our custom one
61
+ # First remove common padding/text classes that might conflict
62
+ current_classes = current_classes.gsub(/\bp-[\d.]+\b/, "")
63
+ .gsub(/\btext-(xs|sm|base|md|lg|xl)\b/, "")
64
+ .gsub(/\brounded-lg\b/, "")
65
+ .strip
66
+
67
+ @props[:class] = "#{current_classes} #{custom_size_class}".strip
68
+ end
69
+
70
+ def current_country
71
+ @@telephone_countries.find { |c| c[:code] == @default_country } || @@telephone_countries.first
72
+ end
73
+
74
+ def country_select
75
+ content_tag :div, class: "relative flex-shrink-0" do
76
+ if @form.present? && @country_attribute.present?
77
+ # Use form builder's select to get proper name attribute
78
+ @form.select(
79
+ @country_attribute,
80
+ options_for_select(
81
+ @@telephone_countries.map { |c| [ "#{c[:flag]} #{c[:dial_code]}", c[:code], { "data-dial-code": c[:dial_code], "data-mask": c[:mask] } ] },
82
+ country_select_value
83
+ ),
84
+ {},
85
+ {
86
+ id: country_select_id,
87
+ class: country_select_classes,
88
+ data: {
89
+ fx_telephone_target: "countrySelect",
90
+ action: "change->fx-telephone#updateMask"
91
+ }
92
+ }
93
+ )
94
+ else
95
+ # Standalone select (no form builder)
96
+ select_tag(
97
+ country_select_name,
98
+ country_options_html,
99
+ id: country_select_id,
100
+ class: country_select_classes,
101
+ data: {
102
+ fx_telephone_target: "countrySelect",
103
+ action: "change->fx-telephone#updateMask"
104
+ }
105
+ )
106
+ end
107
+ end
108
+ end
109
+
110
+ def country_options_html
111
+ safe_join(
112
+ @@telephone_countries.map do |country|
113
+ content_tag :option,
114
+ "#{country[:flag]} #{country[:dial_code]}",
115
+ value: country[:code],
116
+ selected: country[:code] == country_select_value,
117
+ data: {
118
+ dial_code: country[:dial_code],
119
+ mask: country[:mask]
120
+ }
121
+ end
122
+ )
123
+ end
124
+
125
+ def country_select_name
126
+ # Only used in standalone mode
127
+ @country_field_name || "#{@name}_country"
128
+ end
129
+
130
+ def country_select_value
131
+ # Priority:
132
+ # 1. Value from object attribute (when using form builder with country attribute)
133
+ # 2. Default country
134
+ if @form.present? && @country_attribute.present? && @object.present?
135
+ @object.public_send(@country_attribute) rescue @default_country
136
+ else
137
+ @default_country
138
+ end
139
+ end
140
+
141
+ def country_select_id
142
+ "#{@props[:id] || @name}_country"
143
+ end
144
+
145
+ def country_select_classes
146
+ # Get size from config
147
+ size_index = [@sizing, 0].max
148
+ size_index = [size_index, @@telephone_styles[:country_select][:sizes].length - 1].min
149
+ size_config = @@telephone_styles[:country_select][:sizes][size_index]
150
+
151
+ # Get color from config
152
+ color = @color || :default
153
+ color_classes = @@telephone_styles[:country_select][:colors][color] || @@telephone_styles[:country_select][:colors][:default]
154
+
155
+ [
156
+ @@telephone_styles[:country_select][:base],
157
+ @@telephone_styles[:country_select][:width],
158
+ size_config[:padding],
159
+ size_config[:text],
160
+ color_classes
161
+ ].join(" ")
162
+ end
163
+
164
+ def telephone_input
165
+ # Override the props to adjust styling for the telephone input
166
+ input_props = @props.dup
167
+
168
+ # Remove left border radius since it connects to the country select
169
+ current_classes = input_props[:class] || ""
170
+ input_props[:class] = current_classes.gsub("rounded-lg", "rounded-r-lg")
171
+
172
+ # Store original props temporarily
173
+ original_props = @props
174
+ @props = input_props
175
+
176
+ result = input
177
+
178
+ # Restore original props
179
+ @props = original_props
180
+
181
+ result
182
+ end
183
+
184
+ def telephone_input_container
185
+ content_tag :div,
186
+ class: "flex w-full",
187
+ data: {
188
+ controller: "fx-telephone",
189
+ fx_telephone_mask_value: current_country[:mask]
190
+ } do
191
+ safe_join([
192
+ country_select,
193
+ content_tag(:div, telephone_input, class: "relative flex-1")
194
+ ])
195
+ end
196
+ end
197
+ end
@@ -68,7 +68,7 @@ module Fluxbit
68
68
  end
69
69
  end
70
70
 
71
- [ :range, :toggle, :upload_image, :dropzone, :password ].each do |component|
71
+ [ :range, :toggle, :upload_image, :dropzone, :password, :telephone ].each do |component|
72
72
  define_method("fx_#{component}") do |method, **options, &block|
73
73
  options[:error] ||= error_for(method)
74
74
  options[:error] = !!options[:error] if options[:error_hidden] && options[:error]
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fluxbit::Config::Form::TelephoneComponent
4
+ mattr_accessor :default_country, default: "BR"
5
+ mattr_accessor :default_sizing, default: 1
6
+
7
+ # rubocop: disable Layout/LineLength, Metrics/BlockLength
8
+ mattr_accessor :telephone_countries do
9
+ [
10
+ { code: "BR", name: "Brasil", dial_code: "+55", flag: "๐Ÿ‡ง๐Ÿ‡ท", mask: "(##) #####-####" },
11
+ { code: "AR", name: "Argentina", dial_code: "+54", flag: "๐Ÿ‡ฆ๐Ÿ‡ท", mask: "## ####-####" },
12
+ { code: "MX", name: "Mรฉxico", dial_code: "+52", flag: "๐Ÿ‡ฒ๐Ÿ‡ฝ", mask: "## #### ####" },
13
+ { code: "CO", name: "Colombia", dial_code: "+57", flag: "๐Ÿ‡จ๐Ÿ‡ด", mask: "### ### ####" },
14
+ { code: "CL", name: "Chile", dial_code: "+56", flag: "๐Ÿ‡จ๐Ÿ‡ฑ", mask: "# #### ####" },
15
+ { code: "PE", name: "Perรบ", dial_code: "+51", flag: "๐Ÿ‡ต๐Ÿ‡ช", mask: "### ### ###" },
16
+ { code: "VE", name: "Venezuela", dial_code: "+58", flag: "๐Ÿ‡ป๐Ÿ‡ช", mask: "###-###-####" },
17
+ { code: "EC", name: "Ecuador", dial_code: "+593", flag: "๐Ÿ‡ช๐Ÿ‡จ", mask: "## ### ####" },
18
+ { code: "BO", name: "Bolivia", dial_code: "+591", flag: "๐Ÿ‡ง๐Ÿ‡ด", mask: "# ### ####" },
19
+ { code: "PY", name: "Paraguay", dial_code: "+595", flag: "๐Ÿ‡ต๐Ÿ‡พ", mask: "### ### ###" },
20
+ { code: "UY", name: "Uruguay", dial_code: "+598", flag: "๐Ÿ‡บ๐Ÿ‡พ", mask: "# ### ## ##" },
21
+ { code: "CR", name: "Costa Rica", dial_code: "+506", flag: "๐Ÿ‡จ๐Ÿ‡ท", mask: "#### ####" },
22
+ { code: "PA", name: "Panamรก", dial_code: "+507", flag: "๐Ÿ‡ต๐Ÿ‡ฆ", mask: "####-####" },
23
+ { code: "CU", name: "Cuba", dial_code: "+53", flag: "๐Ÿ‡จ๐Ÿ‡บ", mask: "# ### ####" },
24
+ { code: "DO", name: "Repรบblica Dominicana", dial_code: "+1", flag: "๐Ÿ‡ฉ๐Ÿ‡ด", mask: "(###) ###-####" },
25
+ { code: "GT", name: "Guatemala", dial_code: "+502", flag: "๐Ÿ‡ฌ๐Ÿ‡น", mask: "#### ####" },
26
+ { code: "HN", name: "Honduras", dial_code: "+504", flag: "๐Ÿ‡ญ๐Ÿ‡ณ", mask: "####-####" },
27
+ { code: "SV", name: "El Salvador", dial_code: "+503", flag: "๐Ÿ‡ธ๐Ÿ‡ป", mask: "####-####" },
28
+ { code: "NI", name: "Nicaragua", dial_code: "+505", flag: "๐Ÿ‡ณ๐Ÿ‡ฎ", mask: "#### ####" },
29
+ { code: "US", name: "United States", dial_code: "+1", flag: "๐Ÿ‡บ๐Ÿ‡ธ", mask: "(###) ###-####" },
30
+ { code: "CA", name: "Canada", dial_code: "+1", flag: "๐Ÿ‡จ๐Ÿ‡ฆ", mask: "(###) ###-####" },
31
+ { code: "ES", name: "Espaรฑa", dial_code: "+34", flag: "๐Ÿ‡ช๐Ÿ‡ธ", mask: "### ## ## ##" },
32
+ { code: "PT", name: "Portugal", dial_code: "+351", flag: "๐Ÿ‡ต๐Ÿ‡น", mask: "### ### ###" },
33
+ { code: "GB", name: "United Kingdom", dial_code: "+44", flag: "๐Ÿ‡ฌ๐Ÿ‡ง", mask: "#### ### ####" },
34
+ { code: "DE", name: "Germany", dial_code: "+49", flag: "๐Ÿ‡ฉ๐Ÿ‡ช", mask: "### #########" },
35
+ { code: "FR", name: "France", dial_code: "+33", flag: "๐Ÿ‡ซ๐Ÿ‡ท", mask: "# ## ## ## ##" },
36
+ { code: "IT", name: "Italy", dial_code: "+39", flag: "๐Ÿ‡ฎ๐Ÿ‡น", mask: "### ### ####" },
37
+ { code: "JP", name: "Japan", dial_code: "+81", flag: "๐Ÿ‡ฏ๐Ÿ‡ต", mask: "##-####-####" },
38
+ { code: "CN", name: "China", dial_code: "+86", flag: "๐Ÿ‡จ๐Ÿ‡ณ", mask: "### #### ####" },
39
+ { code: "IN", name: "India", dial_code: "+91", flag: "๐Ÿ‡ฎ๐Ÿ‡ณ", mask: "##### #####" },
40
+ { code: "AU", name: "Australia", dial_code: "+61", flag: "๐Ÿ‡ฆ๐Ÿ‡บ", mask: "### ### ###" }
41
+ ]
42
+ end
43
+
44
+ mattr_accessor :telephone_styles do
45
+ {
46
+ country_select: {
47
+ base: "mt-1 block border border-r-0 rounded-l-lg focus:ring-blue-500 focus:border-blue-500",
48
+ colors: {
49
+ default: "text-slate-900 bg-slate-50 border-slate-300 dark:bg-slate-700 dark:border-slate-600 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500",
50
+ success: "text-green-900 bg-green-50 border-green-300 dark:bg-green-700 dark:border-green-600 dark:text-white",
51
+ danger: "text-red-900 bg-red-50 border-red-300 dark:bg-red-700 dark:border-red-600 dark:text-white",
52
+ warning: "text-yellow-900 bg-yellow-50 border-yellow-300 dark:bg-yellow-700 dark:border-yellow-600 dark:text-white",
53
+ info: "text-cyan-900 bg-cyan-50 border-cyan-300 dark:bg-cyan-700 dark:border-cyan-600 dark:text-white"
54
+ },
55
+ width: "w-24",
56
+ sizes: [
57
+ { padding: "p-2", text: "text-xs" }, # Small (0)
58
+ { padding: "p-2.5", text: "text-sm" }, # Medium (1)
59
+ { padding: "p-4", text: "text-base" } # Large (2)
60
+ ]
61
+ },
62
+ input: {
63
+ sizes: [
64
+ "p-2 text-xs rounded-lg", # Small (0)
65
+ "p-2.5 text-sm rounded-lg", # Medium (1)
66
+ "p-4 text-base rounded-lg" # Large (2)
67
+ ]
68
+ }
69
+ }
70
+ end
71
+ # rubocop: enable Layout/LineLength, Metrics/BlockLength
72
+ end
@@ -1,5 +1,5 @@
1
1
  module Fluxbit
2
2
  module ViewComponents
3
- VERSION = "0.4.3"
3
+ VERSION = "0.5.0"
4
4
  end
5
5
  end
@@ -16,6 +16,7 @@ module Fluxbit
16
16
  require "fluxbit/config/form/password_component"
17
17
  require "fluxbit/config/form/radio_group_button_component"
18
18
  require "fluxbit/config/form/range_component"
19
+ require "fluxbit/config/form/telephone_component"
19
20
  require "fluxbit/config/form/text_field_component"
20
21
  require "fluxbit/config/form/toggle_component"
21
22
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluxbit_view_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arthur Molina
@@ -146,6 +146,7 @@ files:
146
146
  - app/assets/javascripts/fluxbit_view_components/row_click_controller.js
147
147
  - app/assets/javascripts/fluxbit_view_components/select_all_controller.js
148
148
  - app/assets/javascripts/fluxbit_view_components/spinner_percent_controller.js
149
+ - app/assets/javascripts/fluxbit_view_components/telephone_controller.js
149
150
  - app/assets/javascripts/fluxbit_view_components/theme_button_controller.js
150
151
  - app/components/fluxbit/accordion_component.rb
151
152
  - app/components/fluxbit/alert_component.rb
@@ -177,6 +178,7 @@ files:
177
178
  - app/components/fluxbit/form/radio_group_button_component.rb
178
179
  - app/components/fluxbit/form/range_component.rb
179
180
  - app/components/fluxbit/form/select_component.rb
181
+ - app/components/fluxbit/form/telephone_component.rb
180
182
  - app/components/fluxbit/form/text_field_component.rb
181
183
  - app/components/fluxbit/form/toggle_component.html.erb
182
184
  - app/components/fluxbit/form/toggle_component.rb
@@ -233,6 +235,7 @@ files:
233
235
  - lib/fluxbit/config/form/password_component.rb
234
236
  - lib/fluxbit/config/form/radio_group_button_component.rb
235
237
  - lib/fluxbit/config/form/range_component.rb
238
+ - lib/fluxbit/config/form/telephone_component.rb
236
239
  - lib/fluxbit/config/form/text_field_component.rb
237
240
  - lib/fluxbit/config/form/toggle_component.rb
238
241
  - lib/fluxbit/config/gravatar_component.rb