rails-active-ui 0.3.8 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +195 -6
  3. data/Rakefile +1 -1
  4. data/app/components/accordion_component.rb +41 -24
  5. data/app/components/accordion_item_component.rb +40 -0
  6. data/app/components/ad_component.rb +1 -1
  7. data/app/components/api_component.rb +1 -1
  8. data/app/components/breadcrumb_component.rb +1 -1
  9. data/app/components/button_group_component.rb +36 -0
  10. data/app/components/button_to_component.rb +1 -1
  11. data/app/components/calendar_component.rb +24 -20
  12. data/app/components/card_component.rb +5 -3
  13. data/app/components/comment_component.rb +5 -3
  14. data/app/components/comment_group_component.rb +27 -0
  15. data/app/components/comment_reply_component.rb +47 -0
  16. data/app/components/comment_reply_group_component.rb +25 -0
  17. data/app/components/dimmer_component.rb +1 -1
  18. data/app/components/divider_component.rb +1 -1
  19. data/app/components/dropdown_component.rb +6 -2
  20. data/app/components/embed_component.rb +1 -1
  21. data/app/components/emoji_component.rb +1 -1
  22. data/app/components/feed_component.rb +16 -4
  23. data/app/components/feed_item_component.rb +85 -0
  24. data/app/components/field_component.rb +42 -0
  25. data/app/components/flag_component.rb +1 -1
  26. data/app/components/flyout_component.rb +3 -2
  27. data/app/components/h_stack_component.rb +1 -1
  28. data/app/components/header_component.rb +2 -2
  29. data/app/components/image_component.rb +1 -1
  30. data/app/components/input_component.rb +22 -41
  31. data/app/components/item_component.rb +3 -2
  32. data/app/components/item_group_component.rb +1 -1
  33. data/app/components/list_component.rb +2 -2
  34. data/app/components/list_content_component.rb +27 -0
  35. data/app/components/list_description_component.rb +17 -0
  36. data/app/components/list_header_component.rb +28 -0
  37. data/app/components/list_item_component.rb +37 -0
  38. data/app/components/loader_component.rb +1 -1
  39. data/app/components/menu_component.rb +1 -1
  40. data/app/components/modal_component.rb +2 -1
  41. data/app/components/nag_component.rb +1 -1
  42. data/app/components/overlay_component.rb +1 -1
  43. data/app/components/placeholder_component.rb +37 -13
  44. data/app/components/popup_component.rb +1 -1
  45. data/app/components/progress_component.rb +1 -1
  46. data/app/components/pusher_component.rb +1 -1
  47. data/app/components/rail_component.rb +1 -1
  48. data/app/components/rating_component.rb +1 -1
  49. data/app/components/reveal_component.rb +3 -2
  50. data/app/components/row_component.rb +4 -0
  51. data/app/components/search_component.rb +1 -1
  52. data/app/components/segment_group_component.rb +1 -1
  53. data/app/components/shape_component.rb +1 -1
  54. data/app/components/sidebar_component.rb +1 -1
  55. data/app/components/site_component.rb +1 -1
  56. data/app/components/slider_component.rb +1 -1
  57. data/app/components/state_component.rb +1 -1
  58. data/app/components/statistic_component.rb +3 -2
  59. data/app/components/step_component.rb +2 -2
  60. data/app/components/step_group_component.rb +1 -1
  61. data/app/components/sticky_component.rb +1 -1
  62. data/app/components/style_component.rb +1 -1
  63. data/app/components/sub_accordion_component.rb +25 -0
  64. data/app/components/sub_header_component.rb +1 -1
  65. data/app/components/sub_menu_component.rb +1 -1
  66. data/app/components/table_cell_component.rb +2 -2
  67. data/app/components/table_component.rb +2 -2
  68. data/app/components/tag_component.rb +57 -0
  69. data/app/components/tag_group_component.rb +33 -0
  70. data/app/components/text_component.rb +1 -1
  71. data/app/components/toast_component.rb +1 -1
  72. data/app/components/transition_component.rb +2 -2
  73. data/app/components/v_stack_component.rb +1 -1
  74. data/app/components/visibility_component.rb +1 -1
  75. data/app/helpers/component_helper.rb +23 -6
  76. data/app/helpers/ui/fomantic_form_builder.rb +155 -201
  77. data/app/javascript/ui/controllers/fui_calendar_controller.js +18 -8
  78. data/lib/ui/version.rb +1 -1
  79. metadata +15 -3
  80. data/app/components/checkbox_component.rb +0 -41
  81. data/app/components/label_component.rb +0 -49
@@ -4,22 +4,25 @@
4
4
  #
5
5
  # A Rails FormBuilder wrapping every helper in Fomantic-UI markup.
6
6
  #
7
- # Usage in a view:
7
+ # All text-like input helpers (text_field, email_field, password_field, etc.)
8
+ # render a bare <input> element by default. Pass `field: true` to wrap the
9
+ # input in a Fomantic <div class="field"> with optional label, error, hint.
8
10
  #
9
- # <%= form_with model: @user, builder: FomanticFormBuilder do |f| %>
10
- # <%= f.text_field :name %>
11
- # <%= f.email_field :email, required: true %>
12
- # <%= f.select :role, [['Admin', 'admin'], ['User', 'user']], dropdown: true %>
13
- # <%= f.check_box :terms, label: 'I agree to the Terms and Conditions' %>
14
- # <%= f.fields_group(equal_width: true) do %>
15
- # <%= f.text_field :first_name %>
16
- # <%= f.text_field :last_name %>
17
- # <% end %>
18
- # <%= f.submit 'Save', color: 'green' %>
19
- # <% end %>
11
+ # Usage:
20
12
  #
21
- # Field options (shared across all helpers):
22
- # label: String – override label text (nil to suppress label)
13
+ # # Bare input (no wrapper):
14
+ # TextField(:email, placeholder: "E-mail address")
15
+ #
16
+ # # Wrapped in <div class="field"> with auto-label:
17
+ # TextField(:email, field: true)
18
+ #
19
+ # # Inside an Input wrapper for icon styling:
20
+ # Input(icon: "user", icon_position: "left") {
21
+ # TextField(:email, placeholder: "E-mail address")
22
+ # }
23
+ #
24
+ # Field options (only used when field: true):
25
+ # label: String – override label text (false to suppress label)
23
26
  # required: Boolean – adds "required" class and asterisk
24
27
  # disabled: Boolean – adds "disabled" class
25
28
  # readonly: Boolean – adds "read-only" class
@@ -29,7 +32,6 @@
29
32
  # warning: String – warning message; adds "warning" class + inline message
30
33
  # hint: String – rendered as a small grey note beneath the input
31
34
  # field_class: String – extra classes on the wrapping .field div
32
- # input_class: String – extra classes on the input element itself
33
35
  #
34
36
  module Ui
35
37
  class FomanticFormBuilder < ActionView::Helpers::FormBuilder
@@ -45,14 +47,46 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
45
47
  %i[
46
48
  text_field email_field password_field
47
49
  number_field url_field telephone_field phone_field
48
- search_field color_field date_field datetime_local_field
49
- month_field week_field time_field range_field
50
+ search_field color_field range_field
50
51
  ].each do |method_name|
51
52
  define_method(method_name) do |attribute, options = {}|
52
- fomantic_field(attribute, options) do |attr, opts|
53
- opts[:class] = class_names("", opts.delete(:input_class))
54
- super(attr, opts)
55
- end
53
+ wrap_in_field = options.delete(:field)
54
+
55
+ input_html = super(attribute, options)
56
+
57
+ wrap_in_field ? fomantic_field(attribute, options, input_html) : input_html
58
+ end
59
+ end
60
+
61
+ # ──────────────────────────────────────────────────────────────────────────
62
+ # Date/time inputs — wrapped in Fomantic calendar
63
+ # ──────────────────────────────────────────────────────────────────────────
64
+
65
+ CALENDAR_TYPE_MAP = {
66
+ date_field: "date",
67
+ datetime_local_field: "datetime",
68
+ time_field: "time",
69
+ month_field: "month",
70
+ week_field: "date"
71
+ }.freeze
72
+
73
+ %i[date_field datetime_local_field time_field month_field week_field].each do |method_name|
74
+ define_method(method_name) do |attribute, options = {}|
75
+ wrap_in_field = options.delete(:field)
76
+ cal_type = options.delete(:calendar_type) || CALENDAR_TYPE_MAP[method_name]
77
+ cal_format = options.delete(:calendar_format)
78
+ cal_icon = options.delete(:icon) || "calendar"
79
+ min_date = options.delete(:min_date)
80
+ max_date = options.delete(:max_date)
81
+ cal_inverted = options.delete(:inverted)
82
+ cal_size = options.delete(:calendar_size)
83
+
84
+ input_html = super(attribute, options)
85
+ result = calendar_wrap(input_html, cal_type,
86
+ format: cal_format, icon: cal_icon, min_date: min_date,
87
+ max_date: max_date, inverted: cal_inverted, size: cal_size)
88
+
89
+ wrap_in_field ? fomantic_field(attribute, options, result) : result
56
90
  end
57
91
  end
58
92
 
@@ -61,12 +95,13 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
61
95
  # ──────────────────────────────────────────────────────────────────────────
62
96
 
63
97
  def text_area(attribute, options = {})
98
+ wrap_in_field = options.delete(:field)
64
99
  transparent = options.delete(:transparent)
65
- fomantic_field(attribute, options) do |attr, opts|
66
- input_class = class_names(("transparent" if transparent), opts.delete(:input_class))
67
- opts[:class] = input_class.presence
68
- super(attr, opts)
69
- end
100
+
101
+ options[:class] = class_names(("transparent" if transparent), options[:class])
102
+ input_html = super(attribute, options)
103
+
104
+ wrap_in_field ? fomantic_field(attribute, options, input_html) : input_html
70
105
  end
71
106
 
72
107
  # ──────────────────────────────────────────────────────────────────────────
@@ -74,25 +109,27 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
74
109
  # ──────────────────────────────────────────────────────────────────────────
75
110
 
76
111
  def emoji_field(attribute, options = {})
77
- fomantic_field(attribute, options) do |attr, opts|
78
- current = object&.public_send(attr) rescue nil
79
- name = object_name ? "#{object_name}[#{attr}]" : attr.to_s
80
-
81
- @template.tag.div(data: { controller: "fui-emoji-picker" }) {
82
- @template.safe_join([
83
- @template.hidden_field_tag(name, current, data: { fui_emoji_picker_target: "input" }),
84
- @template.tag.button(
85
- type: "button",
86
- class: "ui basic button",
87
- data: { fui_emoji_picker_target: "preview", action: "click->fui-emoji-picker#toggle" }
88
- ) { (current.presence || "Pick emoji").html_safe },
89
- @template.tag.div(
90
- style: "display:none; position:absolute; z-index:1000;",
91
- data: { fui_emoji_picker_target: "dropdown" }
92
- )
93
- ])
94
- }
95
- end
112
+ wrap_in_field = options.delete(:field)
113
+
114
+ current = object&.public_send(attribute) rescue nil
115
+ name = object_name ? "#{object_name}[#{attribute}]" : attribute.to_s
116
+
117
+ result = @template.tag.div(data: { controller: "fui-emoji-picker" }) {
118
+ @template.safe_join([
119
+ @template.hidden_field_tag(name, current, data: { fui_emoji_picker_target: "input" }),
120
+ @template.tag.button(
121
+ type: "button",
122
+ class: "ui basic button",
123
+ data: { fui_emoji_picker_target: "preview", action: "click->fui-emoji-picker#toggle" }
124
+ ) { (current.presence || "Pick emoji").html_safe },
125
+ @template.tag.div(
126
+ style: "display:none; position:absolute; z-index:1000;",
127
+ data: { fui_emoji_picker_target: "dropdown" }
128
+ )
129
+ ])
130
+ }
131
+
132
+ wrap_in_field ? fomantic_field(attribute, options, result) : result
96
133
  end
97
134
 
98
135
  # ──────────────────────────────────────────────────────────────────────────
@@ -100,13 +137,13 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
100
137
  # ──────────────────────────────────────────────────────────────────────────
101
138
 
102
139
  def select(attribute, choices = nil, options = {}, html_options = {}, &block)
140
+ wrap_in_field = options.delete(:field)
103
141
  use_dropdown = options.delete(:dropdown)
104
- fomantic_field(attribute, options) do |attr, opts|
105
- merged_html = html_options.merge(class: class_names(html_options[:class], opts.delete(:input_class)))
106
- raw_select = super(attr, choices, opts, merged_html, &block)
107
142
 
108
- use_dropdown ? dropdown_wrap(raw_select) : raw_select
109
- end
143
+ raw_select = super(attribute, choices, options, html_options, &block)
144
+ result = use_dropdown ? dropdown_wrap(raw_select) : raw_select
145
+
146
+ wrap_in_field ? fomantic_field(attribute, options, result) : result
110
147
  end
111
148
 
112
149
  # ──────────────────────────────────────────────────────────────────────────
@@ -114,14 +151,19 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
114
151
  # ──────────────────────────────────────────────────────────────────────────
115
152
 
116
153
  def check_box(attribute, options = {}, checked_value = "1", unchecked_value = "0")
117
- label_text = options.delete(:label) { label_for(attribute) }
118
- kind = options.delete(:kind) { :checkbox } # :checkbox | :slider | :toggle
119
-
120
- fomantic_field(attribute, options.merge(suppress_label: true)) do |attr, opts|
121
- opts.delete(:input_class)
122
- checkbox_html = super(attr, opts, checked_value, unchecked_value)
123
- checkbox_ui(checkbox_html, label_text, kind)
124
- end
154
+ wrap_in_field = options.delete(:field)
155
+ label_text = options.delete(:label) { label_for(attribute) }
156
+ kind = options.delete(:kind) { :checkbox }
157
+ size = options.delete(:size)
158
+ inverted = options.delete(:inverted)
159
+ fitted = options.delete(:fitted)
160
+ right_aligned = options.delete(:right_aligned)
161
+
162
+ checkbox_html = super(attribute, options, checked_value, unchecked_value)
163
+ result = checkbox_ui(checkbox_html, label_text, kind,
164
+ size: size, inverted: inverted, fitted: fitted, right_aligned: right_aligned)
165
+
166
+ wrap_in_field ? fomantic_field(attribute, { suppress_label: true }, result) : result
125
167
  end
126
168
 
127
169
  # ──────────────────────────────────────────────────────────────────────────
@@ -129,13 +171,19 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
129
171
  # ──────────────────────────────────────────────────────────────────────────
130
172
 
131
173
  def radio_button(attribute, value, options = {})
132
- label_text = options.delete(:label) { value.to_s.humanize }
133
-
134
- fomantic_field(attribute, options.merge(suppress_label: true)) do |attr, opts|
135
- opts.delete(:input_class)
136
- radio_html = super(attr, value, opts)
137
- checkbox_ui(radio_html, label_text, :radio)
138
- end
174
+ wrap_in_field = options.delete(:field)
175
+ label_text = options.delete(:label) { value.to_s.humanize }
176
+ kind = options.delete(:kind) { :radio }
177
+ size = options.delete(:size)
178
+ inverted = options.delete(:inverted)
179
+ fitted = options.delete(:fitted)
180
+ right_aligned = options.delete(:right_aligned)
181
+
182
+ radio_html = super(attribute, value, options)
183
+ result = checkbox_ui(radio_html, label_text, kind,
184
+ size: size, inverted: inverted, fitted: fitted, right_aligned: right_aligned)
185
+
186
+ wrap_in_field ? fomantic_field(attribute, { suppress_label: true }, result) : result
139
187
  end
140
188
 
141
189
  # ──────────────────────────────────────────────────────────────────────────
@@ -143,14 +191,15 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
143
191
  # ──────────────────────────────────────────────────────────────────────────
144
192
 
145
193
  def file_field(attribute, options = {})
146
- fomantic_field(attribute, options) do |attr, opts|
147
- opts.delete(:input_class)
148
- super(attr, opts)
149
- end
194
+ wrap_in_field = options.delete(:field)
195
+
196
+ input_html = super(attribute, options)
197
+
198
+ wrap_in_field ? fomantic_field(attribute, options, input_html) : input_html
150
199
  end
151
200
 
152
201
  # ──────────────────────────────────────────────────────────────────────────
153
- # Hidden field (no wrapper)
202
+ # Hidden field (no wrapper ever)
154
203
  # ──────────────────────────────────────────────────────────────────────────
155
204
 
156
205
  def hidden_field(attribute, options = {})
@@ -183,7 +232,7 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
183
232
  end
184
233
 
185
234
  # ──────────────────────────────────────────────────────────────────────────
186
- # Label override — produces a plain <label> (called internally too)
235
+ # Label override — produces a plain <label>
187
236
  # ──────────────────────────────────────────────────────────────────────────
188
237
 
189
238
  def label(attribute, text = nil, options = {}, &block)
@@ -192,11 +241,6 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
192
241
 
193
242
  # ──────────────────────────────────────────────────────────────────────────
194
243
  # fields_group — wraps children in <div class="fields ...">
195
- #
196
- # Options:
197
- # equal_width: Boolean – adds "equal width"
198
- # inline: Boolean – adds "inline"
199
- # count: Integer – "N fields" (evenly divided)
200
244
  # ──────────────────────────────────────────────────────────────────────────
201
245
 
202
246
  def fields_group(options = {}, &block)
@@ -240,9 +284,9 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
240
284
 
241
285
  private
242
286
 
243
- # ── Core field wrapper ─────────────────────────────────────────────────────
287
+ # ── Field wrapper (opt-in via field: true) ─────────────────────────────────
244
288
 
245
- def fomantic_field(attribute, options, &block)
289
+ def fomantic_field(attribute, options, input_html)
246
290
  required = options.delete(:required)
247
291
  disabled = options.delete(:disabled)
248
292
  readonly_opt = options.delete(:readonly)
@@ -275,8 +319,6 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
275
319
  text ? label(attribute, text) : nil
276
320
  end
277
321
 
278
- input_html = block.call(attribute, options)
279
-
280
322
  note_html = inline_note(error_msg || (has_error && first_error(attribute)), "red")
281
323
  note_html = inline_note(warning_msg, "orange") if note_html.blank? && has_warning
282
324
  note_html = inline_note(hint, "grey") if note_html.blank? && hint
@@ -288,15 +330,44 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
288
330
 
289
331
  # ── Checkbox / Radio UI shell ──────────────────────────────────────────────
290
332
 
291
- def checkbox_ui(input_html, label_text, kind)
333
+ def checkbox_ui(input_html, label_text, kind, size: nil, inverted: nil, fitted: nil, right_aligned: nil)
292
334
  modifier = { radio: "radio", slider: "slider", toggle: "toggle" }[kind]
293
- css = class_names("ui", modifier, "checkbox")
335
+ css = class_names(
336
+ "ui",
337
+ size,
338
+ modifier,
339
+ ("inverted" if inverted),
340
+ ("fitted" if fitted),
341
+ ("right aligned" if right_aligned),
342
+ "checkbox"
343
+ )
294
344
 
295
- @template.tag.div(class: css) do
345
+ @template.tag.div(class: css, data: { controller: "fui-checkbox" }) do
296
346
  safe_join([ input_html, @template.tag.label(label_text.is_a?(String) ? label_text : nil) ])
297
347
  end
298
348
  end
299
349
 
350
+ # ── Calendar wrapper ─────────────────────────────────────────────────────
351
+
352
+ def calendar_wrap(input_html, cal_type, format: nil, icon: "calendar",
353
+ min_date: nil, max_date: nil, inverted: nil, size: nil)
354
+ data = { controller: "fui-calendar", fui_calendar_type_value: cal_type }
355
+ data[:fui_calendar_format_value] = format if format
356
+ data[:fui_calendar_min_date_value] = min_date if min_date
357
+ data[:fui_calendar_max_date_value] = max_date if max_date
358
+
359
+ cal_class = class_names("ui", size, ("inverted" if inverted), "calendar")
360
+
361
+ @template.tag.div(class: cal_class, data: data) {
362
+ @template.tag.div(class: "ui fluid input left icon") {
363
+ safe_join([
364
+ @template.tag.i(class: "#{icon} icon"),
365
+ input_html
366
+ ])
367
+ }
368
+ }
369
+ end
370
+
300
371
  # ── Dropdown wrapper ───────────────────────────────────────────────────────
301
372
 
302
373
  def dropdown_wrap(select_html)
@@ -361,127 +432,10 @@ class FomanticFormBuilder < ActionView::Helpers::FormBuilder
361
432
  @template.safe_join(parts)
362
433
  end
363
434
 
364
- # ── class_names helper (Rails 6.1+ ships this) ────────────────────────────
435
+ # ── class_names helper ────────────────────────────────────────────────────
365
436
 
366
437
  def class_names(*args)
367
438
  args.flatten.compact.reject { |v| v == false || v.to_s.strip.empty? }.join(" ")
368
439
  end
369
440
  end
370
-
371
- # ─────────────────────────────────────────────────────────────────────────────
372
- # Usage examples (views)
373
- # ─────────────────────────────────────────────────────────────────────────────
374
-
375
- # ── 1. Basic user registration form ──────────────────────────────────────────
376
- #
377
- # <%= form_with model: @user, builder: FomanticFormBuilder, class: "ui form" do |f| %>
378
- #
379
- # <%# grouped equal-width row %>
380
- # <%= f.fields_group(equal_width: true) do %>
381
- # <%= f.text_field :first_name, required: true %>
382
- # <%= f.text_field :last_name, required: true %>
383
- # <% end %>
384
- #
385
- # <%= f.email_field :email, required: true %>
386
- # <%= f.password_field :password, required: true,
387
- # hint: "Minimum 8 characters" %>
388
- #
389
- # <%# Native select styled by Fomantic %>
390
- # <%= f.select :role, [["Admin", "admin"], ["Member", "member"]],
391
- # { prompt: "Select a role" } %>
392
- #
393
- # <%# Fomantic dropdown widget (adds JS .dropdown() wrapper) %>
394
- # <%= f.select :country, country_options,
395
- # { dropdown: true, required: true } %>
396
- #
397
- # <%= f.check_box :terms,
398
- # label: "I agree to the Terms and Conditions",
399
- # required: true %>
400
- #
401
- # <%# Error/success messages from model %>
402
- # <%= f.error_message "We had some issues",
403
- # @user.errors.full_messages if @user.errors.any? %>
404
- #
405
- # <%= f.submit "Sign up", color: "green" %>
406
- # <% end %>
407
-
408
-
409
- # ── 2. Inline field (label beside input) ─────────────────────────────────────
410
- #
411
- # <%= f.text_field :phone, label: "Phone Number", inline: true,
412
- # width: "eight" %>
413
-
414
-
415
- # ── 3. Width-constrained fields ───────────────────────────────────────────────
416
- #
417
- # <%= f.fields_group do %>
418
- # <%= f.text_field :first_name, width: "six" %>
419
- # <%= f.text_field :middle, width: "three" %>
420
- # <%= f.text_field :last_name, width: "seven" %>
421
- # <% end %>
422
-
423
-
424
- # ── 4. Checkbox kinds ─────────────────────────────────────────────────────────
425
- #
426
- # <%= f.check_box :notifications, label: "Enable notifications", kind: :toggle %>
427
- # <%= f.check_box :public, label: "Publicly visible", kind: :slider %>
428
-
429
-
430
- # ── 5. Radio group ────────────────────────────────────────────────────────────
431
- #
432
- # <%= f.fields_group do %>
433
- # <%= f.radio_button :plan, "basic", label: "Basic" %>
434
- # <%= f.radio_button :plan, "pro", label: "Pro" %>
435
- # <%= f.radio_button :plan, "enterprise", label: "Enterprise" %>
436
- # <% end %>
437
-
438
-
439
- # ── 6. Textarea ───────────────────────────────────────────────────────────────
440
- #
441
- # <%= f.text_area :bio, rows: 4 %>
442
- # <%= f.text_area :description, rows: 2, transparent: true %>
443
-
444
-
445
- # ── 7. Form-level state messages ──────────────────────────────────────────────
446
- #
447
- # <%= f.error_message "Action Forbidden", ["Email already registered"] %>
448
- # <%= f.success_message "All done!", "Your profile has been updated." %>
449
- # <%= f.warning_message "Heads up", ["Please verify your email"] %>
450
- # <%= f.info_message "Password rules", ["Must be at least 8 characters"] %>
451
-
452
-
453
- # ── 8. Submit variations ──────────────────────────────────────────────────────
454
- #
455
- # <%= f.submit "Save", color: "blue" %>
456
- # <%= f.submit "Delete", color: "red", basic: true %>
457
- # <%= f.submit "Go", color: "green", size: "large", icon: "checkmark" %>
458
-
459
-
460
- # ── 9. Opt-in per form (when default builder is not set) ──────────────────────
461
- #
462
- # <%= form_with model: @post, builder: FomanticFormBuilder, class: "ui form" do |f| %>
463
- # ...
464
- # <% end %>
465
-
466
-
467
- # ─────────────────────────────────────────────────────────────────────────────
468
- # JavaScript initialisation (application.js or a Stimulus controller)
469
- # ─────────────────────────────────────────────────────────────────────────────
470
- #
471
- # // Initialise all Fomantic dropdowns on the page
472
- # document.addEventListener("DOMContentLoaded", () => {
473
- # $(".ui.dropdown").dropdown();
474
- # $(".ui.checkbox").checkbox();
475
- # $(".ui.calendar").calendar({ type: "date" });
476
- # });
477
- #
478
- # // Or with a Stimulus controller:
479
- # //
480
- # // import { Controller } from "@hotwired/stimulus"
481
- # // export default class extends Controller {
482
- # // connect() {
483
- # // $(this.element).find(".ui.dropdown").dropdown()
484
- # // $(this.element).find(".ui.checkbox").checkbox()
485
- # // }
486
- # // }
487
441
  end
@@ -14,14 +14,15 @@ import { Controller } from "@hotwired/stimulus"
14
14
  //
15
15
  export default class extends Controller {
16
16
  static values = {
17
- type: { type: String, default: "date" },
18
- closable: { type: Boolean, default: true },
19
- monthFirst: { type: Boolean, default: true },
20
- firstDayOfWeek: { type: Number, default: 0 },
21
- today: { type: Boolean, default: false },
22
- formatInput: { type: Boolean, default: true },
23
- minDate: { type: String, default: "" },
24
- maxDate: { type: String, default: "" },
17
+ type: { type: String, default: "date" },
18
+ format: { type: String, default: "" },
19
+ closable: { type: Boolean, default: true },
20
+ monthFirst: { type: Boolean, default: true },
21
+ firstDayOfWeek: { type: Number, default: 0 },
22
+ today: { type: Boolean, default: false },
23
+ formatInput: { type: Boolean, default: true },
24
+ minDate: { type: String, default: "" },
25
+ maxDate: { type: String, default: "" },
25
26
  }
26
27
 
27
28
  connect() {
@@ -58,6 +59,15 @@ export default class extends Controller {
58
59
  onHide: () => { this.dispatch("hide") },
59
60
  }
60
61
 
62
+ if (this.formatValue) {
63
+ opts.formatter = {
64
+ date: this.formatValue,
65
+ datetime: this.formatValue,
66
+ time: this.formatValue,
67
+ cellTime: this.formatValue,
68
+ }
69
+ }
70
+
61
71
  if (this.minDateValue) opts.minDate = new Date(this.minDateValue)
62
72
  if (this.maxDateValue) opts.maxDate = new Date(this.maxDateValue)
63
73
 
data/lib/ui/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ui
2
- VERSION = "0.3.8"
2
+ VERSION = "0.4.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-active-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - nathan
@@ -43,17 +43,21 @@ files:
43
43
  - app/assets/stylesheets.css
44
44
  - app/blocks/resource_list_block.rb
45
45
  - app/components/accordion_component.rb
46
+ - app/components/accordion_item_component.rb
46
47
  - app/components/ad_component.rb
47
48
  - app/components/api_component.rb
48
49
  - app/components/back_button_component.rb
49
50
  - app/components/breadcrumb_component.rb
50
51
  - app/components/button_component.rb
52
+ - app/components/button_group_component.rb
51
53
  - app/components/button_to_component.rb
52
54
  - app/components/calendar_component.rb
53
55
  - app/components/card_component.rb
54
- - app/components/checkbox_component.rb
55
56
  - app/components/column_component.rb
56
57
  - app/components/comment_component.rb
58
+ - app/components/comment_group_component.rb
59
+ - app/components/comment_reply_component.rb
60
+ - app/components/comment_reply_group_component.rb
57
61
  - app/components/concerns/alignable.rb
58
62
  - app/components/concerns/attachable.rb
59
63
  - app/components/concerns/orientable.rb
@@ -66,6 +70,8 @@ files:
66
70
  - app/components/embed_component.rb
67
71
  - app/components/emoji_component.rb
68
72
  - app/components/feed_component.rb
73
+ - app/components/feed_item_component.rb
74
+ - app/components/field_component.rb
69
75
  - app/components/flag_component.rb
70
76
  - app/components/flyout_component.rb
71
77
  - app/components/form_component.rb
@@ -77,9 +83,12 @@ files:
77
83
  - app/components/input_component.rb
78
84
  - app/components/item_component.rb
79
85
  - app/components/item_group_component.rb
80
- - app/components/label_component.rb
81
86
  - app/components/link_to_component.rb
82
87
  - app/components/list_component.rb
88
+ - app/components/list_content_component.rb
89
+ - app/components/list_description_component.rb
90
+ - app/components/list_header_component.rb
91
+ - app/components/list_item_component.rb
83
92
  - app/components/loader_component.rb
84
93
  - app/components/menu_component.rb
85
94
  - app/components/menu_item_component.rb
@@ -110,6 +119,7 @@ files:
110
119
  - app/components/step_group_component.rb
111
120
  - app/components/sticky_component.rb
112
121
  - app/components/style_component.rb
122
+ - app/components/sub_accordion_component.rb
113
123
  - app/components/sub_header_component.rb
114
124
  - app/components/sub_menu_component.rb
115
125
  - app/components/tab_component.rb
@@ -117,6 +127,8 @@ files:
117
127
  - app/components/table_cell_component.rb
118
128
  - app/components/table_component.rb
119
129
  - app/components/table_row_component.rb
130
+ - app/components/tag_component.rb
131
+ - app/components/tag_group_component.rb
120
132
  - app/components/template_component.rb
121
133
  - app/components/text_component.rb
122
134
  - app/components/toast_component.rb
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Checkbox — checkboxes, radios, toggles, sliders.
4
- #
5
- # Usage:
6
- # Checkbox(label_text: "Accept terms", name: "terms")
7
- # Checkbox(type: :toggle, label_text: "Dark mode", checked: true)
8
- # Checkbox(type: :radio, label_text: "Option A", name: "choice", value: "a")
9
-
10
- class CheckboxComponent < Component
11
- attribute :type, :string, default: "checkbox"
12
- attribute :label_text, :string, default: nil
13
- attribute :name, :string, default: nil
14
- attribute :value, :string, default: nil
15
- attribute :checked, :boolean, default: false
16
- attribute :disabled, :boolean, default: false
17
- attribute :read_only, :boolean, default: false
18
- attribute :fitted, :boolean, default: false
19
-
20
- def to_s
21
- classes = class_names(
22
- "ui",
23
- (type unless type == "checkbox"),
24
- { "fitted" => fitted, "read-only" => read_only, "disabled" => disabled },
25
- "checkbox"
26
- )
27
-
28
- field_type = type == "radio" ? "radio" : "checkbox"
29
- input_opts = { type: field_type }
30
- input_opts[:name] = name if name
31
- input_opts[:value] = value if value
32
- input_opts[:checked] = "checked" if checked
33
- input_opts[:disabled] = "disabled" if disabled
34
-
35
- label_el = tag.label { label_text || "" }
36
-
37
- tag.div(class: classes, data: { controller: "fui-checkbox" }) {
38
- safe_join([ tag.input(**input_opts), label_el ])
39
- }
40
- end
41
- end