custom_table 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +210 -0
  3. data/Rakefile +8 -0
  4. data/app/assets/config/custom_table_manifest.js +1 -0
  5. data/app/assets/stylesheets/custom_table/application.scss +1 -0
  6. data/app/assets/stylesheets/custom_table/table.scss +89 -0
  7. data/app/controllers/concerns/custom_table_concern.rb +118 -0
  8. data/app/controllers/custom_table/application_controller.rb +4 -0
  9. data/app/controllers/custom_table/settings_controller.rb +86 -0
  10. data/app/helpers/custom_table/application_helper.rb +483 -0
  11. data/app/helpers/custom_table/fieldset_helper.rb +90 -0
  12. data/app/helpers/custom_table/icons_helper.rb +42 -0
  13. data/app/inputs/date_picker_input.rb +52 -0
  14. data/app/javascript/controllers/batch_actions_controller.js +53 -0
  15. data/app/javascript/controllers/table_controller.js +109 -0
  16. data/app/jobs/custom_table/application_job.rb +4 -0
  17. data/app/mailers/custom_table/application_mailer.rb +6 -0
  18. data/app/models/concerns/custom_table_settings.rb +42 -0
  19. data/app/models/custom_table/application_record.rb +5 -0
  20. data/app/views/custom_table/_download.haml +19 -0
  21. data/app/views/custom_table/_field.haml +8 -0
  22. data/app/views/custom_table/_field_plain.haml +1 -0
  23. data/app/views/custom_table/_fieldset.haml +2 -0
  24. data/app/views/custom_table/_filter.html.haml +104 -0
  25. data/app/views/custom_table/_settings.html.haml +57 -0
  26. data/app/views/custom_table/_table.html.haml +261 -0
  27. data/app/views/custom_table/_table.xlsx.axlsx +76 -0
  28. data/app/views/custom_table/_table_fe.xlsx.fast_excel +141 -0
  29. data/app/views/custom_table/_table_row.html.haml +72 -0
  30. data/app/views/custom_table/_table_row_data.html.haml +26 -0
  31. data/app/views/custom_table/settings/destroy.html.haml +4 -0
  32. data/app/views/custom_table/settings/edit.html.haml +2 -0
  33. data/app/views/custom_table/settings/update.html.haml +4 -0
  34. data/app/views/layouts/custom_table/application.html.erb +15 -0
  35. data/config/initializers/simple_form_bootstrap.rb +468 -0
  36. data/config/locales/en.yml +18 -0
  37. data/config/locales/ru.yml +18 -0
  38. data/config/routes.rb +5 -0
  39. data/lib/custom_table/configuration.rb +10 -0
  40. data/lib/custom_table/engine.rb +12 -0
  41. data/lib/custom_table/version.rb +3 -0
  42. data/lib/custom_table.rb +14 -0
  43. data/lib/generators/custom_table/USAGE +8 -0
  44. data/lib/generators/custom_table/custom_table_generator.rb +16 -0
  45. data/lib/generators/custom_table/templates/initializer.rb +3 -0
  46. data/lib/generators/custom_table/templates/migration.rb +5 -0
  47. data/lib/tasks/custom_table_tasks.rake +4 -0
  48. metadata +300 -0
@@ -0,0 +1,483 @@
1
+ module CustomTable
2
+ module ApplicationHelper
3
+
4
+ # MOVE TO GENERATED HELPER
5
+ # def boolean_icon(value, true_value = nil, false_value = nil)
6
+ # capture do
7
+ # concat content_tag(:i, "", class: (value ? "bi bi-check-lg text-success" : "bi bi-x-lg text-danger"), data: {raw: value})
8
+ # concat content_tag(:span, value ? true_value : false_value, class: "ms-1") unless true_value.nil?
9
+ # end
10
+ # end
11
+
12
+ def custom_table_form_for(record, options = {}, &block)
13
+ options[:url] = request.path if options[:url].nil?
14
+ options[:method] = :get
15
+
16
+ options[:html] ||= {}
17
+ options[:html][:class] = "row row-cols-md-auto g-3 align-items-center custom-table-filter"
18
+ options[:wrapper] = options[:wrapper] || :inline_form
19
+
20
+ options[:wrapper_mappings] = {
21
+ boolean: :inline_boolean,
22
+ check_boxes: :vertical_collection,
23
+ radio_buttons: :vertical_collection,
24
+ date: :inline_element,
25
+ select: :inline_select
26
+ }
27
+
28
+ simple_form_for(record, options, &block)
29
+ end
30
+
31
+ def fields_table_for(collection, options = {}, &block); end
32
+
33
+ # Tries to fetch attribute value by all possible ways (by priority):
34
+ # Helper {singular_model_name}_{field}_field
35
+ # Helper {singular_model_name}_{field}
36
+ # Helper {singular_model_name}_{field}_raw
37
+ # Attribute of model
38
+ def field_value_for item, field, definitions: nil, variant: nil
39
+
40
+ defs = definitions
41
+
42
+ model_name = item.model_name.singular
43
+ global_model_name = item.class.model_name.singular # non-model
44
+
45
+ helpers = []
46
+ helpers += [defs[:helper]] if !defs.nil?
47
+
48
+ helpers += [
49
+ "#{model_name}_#{variant}_#{field}_field",
50
+ "#{model_name}_#{variant}_#{field}",
51
+ ] if !variant.nil?
52
+
53
+ helpers += [
54
+ "#{model_name}_#{field}_field",
55
+ "#{model_name}_#{field}",
56
+ "#{model_name}_#{field}_raw",
57
+ "#{global_model_name}_#{field}"
58
+ ]
59
+
60
+ if item.class.superclass.to_s != "ApplicationRecord"
61
+ super_model_name = item.class.superclass.model_name.singular
62
+ helpers += [
63
+ "#{super_model_name}_#{field}_field",
64
+ "#{super_model_name}_#{field}",
65
+ "#{super_model_name}_#{field}_raw",
66
+ ]
67
+ end
68
+
69
+ helpers = helpers.flatten.compact
70
+
71
+ helpers.each do |helper|
72
+ if self.class.method_defined?(helper)
73
+ if self.method(helper).arity == 1 || self.method(helper).arity == -2
74
+ return self.send(helper, item) || not_set
75
+ end
76
+ if self.method(helper).arity == 2
77
+ return self.send(helper, item, field) || not_set
78
+ end
79
+ end
80
+ end
81
+
82
+ if !defs.nil? && defs[:amount]
83
+ if !item.class.columns_hash[field.to_s].nil? && item.class.columns_hash[field.to_s].type == :integer
84
+ return amount_value(item.send(field), 0) rescue ""
85
+ else
86
+ return amount(item.send(field)) rescue ""
87
+ end
88
+ else
89
+ if item.class.reflect_on_association(field)
90
+ return not_set if (item.send(field) rescue nil).nil?
91
+ return render(item.send(field)) rescue item.send(field).to_s rescue ""
92
+ elsif item.class.columns_hash[field.to_s] && item.class.columns_hash[field.to_s].type == :boolean
93
+ return boolean_icon(item.send(field)) rescue ""
94
+ elsif item.class.defined_enums.has_key?(field.to_s)
95
+ return (item.send("human_#{field}") rescue (item.send(field).presence || not_set)).to_s rescue ""
96
+ elsif item.class.columns_hash[field.to_s] && [:date, :datetime].include?(item.class.columns_hash[field.to_s].type)
97
+ return (item.send(field).blank? ? not_set : l(item.send(field), format: :short)) rescue ""
98
+ elsif item.class.columns_hash[field.to_s] && [:integer, :float, :decimal].include?(item.class.columns_hash[field.to_s].type)
99
+ return not_set if (item.send(field) rescue nil).nil?
100
+ return amount(item.send(field)) rescue ""
101
+ else
102
+ return (item.send(field).presence || not_set).to_s rescue ""
103
+ end
104
+ end
105
+
106
+ end
107
+
108
+ def custom_table_totals collection, fields, totals = {}, fields_totals = {}
109
+
110
+ out = {}
111
+ totals = {} if totals.nil?
112
+ fields_totals = {} if fields_totals.nil?
113
+
114
+ fields.keys.each do |field|
115
+
116
+ # We only work if field total is enabled explicitly with definition or totals
117
+ next if !fields[field][:total] && !totals.has_key?(field)
118
+
119
+ # Taking pre-defined value if available
120
+ if !totals[field].nil?
121
+ out[field] = totals[field]
122
+ else
123
+ model_class = collection.model
124
+ # Trying to find helper first
125
+ model_name = model_class.model_name.singular
126
+ helper = "#{model_name}_#{field}_total"
127
+
128
+ if self.class.method_defined?(helper)
129
+ # Better use helper!
130
+ out[field] = self.send(helper, collection.except(:limit, :offset, :order, :group))
131
+ else
132
+ if collection.respond_to?(:total_pages) && (!model_class.columns_hash[field.to_s].nil? || !fields[field][:total_scope].nil?)
133
+ # We can try to sum value from database
134
+
135
+ if fields[field][:total_scope].nil?
136
+ out[field] = collection.except(:limit, :offset, :order, :group).sum(field)
137
+ else
138
+ out[field] = collection.except(:limit, :offset, :order, :group).send(fields[field][:total_scope])
139
+ end
140
+ else
141
+ # Taking simple summed value because all data is shown
142
+ out[field] = fields_totals[field]
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+ end
149
+
150
+ out
151
+
152
+ end
153
+
154
+ # Same as above but for Export only
155
+ def raw_field_value_for item, field, definitions: nil, variant: nil
156
+
157
+ defs = definitions
158
+
159
+ model_name = item.model_name.singular
160
+ global_model_name = item.class.model_name.singular
161
+
162
+ helpers = []
163
+
164
+ helpers += [
165
+ "#{model_name}_#{variant}_#{field}_field_raw",
166
+ "#{model_name}_#{variant}_#{field}_raw",
167
+ ] if !variant.nil?
168
+
169
+ helpers += [
170
+ "#{model_name}_#{field}_field_raw",
171
+ "#{model_name}_#{field}_raw",
172
+ "#{global_model_name}_#{field}_raw"
173
+ ]
174
+
175
+ if item.class.superclass.to_s != "ApplicationRecord"
176
+ super_model_name = item.class.superclass.model_name.singular
177
+ helpers += [
178
+ "#{super_model_name}_#{field}_field_raw",
179
+ "#{super_model_name}_#{field}_raw",
180
+ ]
181
+ end
182
+
183
+ helpers = helpers.flatten.compact
184
+
185
+ helpers.each do |helper|
186
+ return self.send(helper, item) if self.class.method_defined?(helper)
187
+ end
188
+
189
+ if !defs.nil? && defs[:amount]
190
+ return item.send(field) rescue nil
191
+ else
192
+ if item.class.reflect_on_association(field)
193
+ return item.send(field).to_s rescue nil
194
+ elsif item.class.columns_hash[field.to_s] && item.class.columns_hash[field.to_s].type == :boolean
195
+ return item.send(field) rescue nil
196
+ elsif item.class.defined_enums.has_key?(field.to_s)
197
+ return (item.send(field).presence) rescue nil
198
+ elsif item.class.columns_hash[field.to_s] && [:date, :datetime].include?(item.class.columns_hash[field.to_s].type)
199
+ return (item.send(field).presence) rescue nil
200
+ elsif item.class.columns_hash[field.to_s] && [:float, :decimal].include?(item.class.columns_hash[field.to_s].type)
201
+ return item.send(field).round(2) rescue nil
202
+ elsif item.class.columns_hash[field.to_s] && [:integer].include?(item.class.columns_hash[field.to_s].type)
203
+ return item.send(field) rescue nil
204
+ else
205
+ return (item.send(field).presence) rescue nil
206
+ end
207
+ end
208
+
209
+ end
210
+
211
+
212
+ # Returns list of fields to show in table according user settings
213
+ def custom_table_fields_for(model, variant: nil, current_search: {}, predefined_fields: nil, use_all_fields: false)
214
+
215
+ fields = []
216
+ current_search = {} if current_search.nil?
217
+
218
+ model_fields = custom_table_fields_definition_for(model, variant)
219
+
220
+ if (!predefined_fields.nil?)
221
+ out = {}
222
+ predefined_fields.each do |f|
223
+ out[f] = model_fields[f]
224
+ end
225
+ return out.compact
226
+ # return model_fields.select {|k,v| predefined_fields.include?(k) }
227
+ end
228
+
229
+ fields_key = model.model_name.to_s
230
+ fields_key += "-#{variant}" unless variant.nil?
231
+
232
+ s = custom_table_user_customized_fields_for(model, variant)
233
+
234
+ use_all_fields = true if params[:custom_table_use_all_fields]
235
+
236
+ if use_all_fields
237
+ selected_fields = model_fields.keys
238
+ else
239
+ if !s.nil?
240
+ # Use saved user settings for the model
241
+ always_selected_fields = model_fields.select { |x, y| [:always].include?(y[:appear]) || current_search.keys.include?(x.to_s) }.keys
242
+ always_selected_fields.each {|f| s = {"#{f.to_s}": true}.merge(s) if s[f].nil? }
243
+ selected_fields = s.select{|f,v| v }.keys
244
+ else
245
+ # Use default model settings
246
+ selected_fields = model_fields.select { |x, y| [:always, :default].include?(y[:appear]) || current_search.keys.include?(x.to_s) }.keys
247
+ end
248
+ end
249
+
250
+ # Filter selection again with model settings for sure
251
+ return selected_fields.collect{|f| [f, model_fields[f]]}.to_h
252
+
253
+ end
254
+
255
+ # Returns list of fields for customization form
256
+ def custom_table_fields_settings_for(model, variant: nil)
257
+
258
+ model_fields = custom_table_fields_definition_for(model, variant)
259
+ model_fields = model_fields.reject {|k,v| [:export, :never].include?(v[:appear]) }
260
+
261
+ fields_key = model.model_name.to_s
262
+ fields_key += "-#{variant}" unless variant.nil?
263
+
264
+ user_customization = custom_table_user_customized_fields_for(model, variant)
265
+
266
+ if !user_customization.nil?
267
+ # Add new fields at the top to user customization if not present
268
+ model_fields.each do |f, v|
269
+ next if !user_customization[f].nil?
270
+ selected = [:always, :default].include?(v[:appear])
271
+ new_field = {"#{f.to_s}": selected}
272
+ if selected
273
+ user_customization = new_field.merge(user_customization)
274
+ else
275
+ user_customization = user_customization.merge(new_field)
276
+ end
277
+ end
278
+ return user_customization.reject{|f,v| model_fields[f].nil?}.collect{|f,v| [f, {selected: v}.merge(model_fields[f])]}.to_h
279
+ else
280
+ # Use default model settings
281
+ # abort model_fields.transform_values!{|f| f[:selected] = true}.inspect
282
+ return model_fields.each{|k,f| f[:selected] = [:always, :default].include?(f[:appear])}
283
+ end
284
+
285
+
286
+ end
287
+
288
+ # Prepares object of user fields customization
289
+ def custom_table_user_customized_fields_for(model, variant = nil)
290
+ fields_key = custom_table_fields_key(model, variant)
291
+ defs = custom_table_fields_definition_for(model, variant)
292
+ return nil if current_user.nil? || current_user.custom_table.nil? || current_user.custom_table.dig(fields_key, :fields).nil?
293
+ return current_user.custom_table.dig(fields_key, :fields).symbolize_keys.reject{|k,v| defs[k.to_sym].nil?}
294
+
295
+ end
296
+
297
+ # Prepares object of user search customization
298
+ def custom_table_user_customization_for(model, variant = nil)
299
+ fields_key = custom_table_fields_key(model, variant)
300
+ return nil if current_user.nil? || current_user.custom_table.nil?
301
+ return current_user.custom_table[fields_key]&.symbolize_keys
302
+ end
303
+
304
+ def custom_table_fields_key(model, variant = nil)
305
+ fields_key = model.model_name.to_s
306
+ fields_key += "-#{variant}" unless variant.nil?
307
+ return fields_key
308
+ end
309
+
310
+ def custom_table_customizable_fields_for(model, variant = nil)
311
+ model_fields = custom_table_fields_definition_for(model, variant)
312
+ model_fields.reject {|k,v| [:always, :export, :never].include?(v[:appear]) }
313
+ end
314
+
315
+ # Base definition for model
316
+ def custom_table_fields_definition_for(model, variant = nil)
317
+ helper_name = "#{model.model_name.singular}_custom_table_fields"
318
+ if (! self.class.method_defined?(helper_name))
319
+ raise "#{helper_name} helper is not defined so we do not know how to render custom_table for #{model}"
320
+ end
321
+
322
+ if variant.nil? || method(helper_name).parameters.empty?
323
+ defs = self.send("#{helper_name}")
324
+ else
325
+ defs = self.send("#{helper_name}", variant)
326
+ end
327
+
328
+ return defs.each{|x,y| y[:label] = model.human_attribute_name(x) if y[:label].nil? }
329
+ end
330
+
331
+ # Base definition for model
332
+ def custom_table_fields_definition_for_field(model, field, variant = nil)
333
+ helper_name = "#{model.model_name.singular}_custom_table_fields"
334
+ if (! self.class.method_defined?(helper_name))
335
+ raise "#{helper_name} helper is not defined so we do not know how to render custom_table for #{model}"
336
+ end
337
+ if variant.nil? || method(helper_name).parameters.empty?
338
+ defs = self.send("#{helper_name}")
339
+ else
340
+ defs = self.send("#{helper_name}", variant)
341
+ end
342
+ return nil if defs[field].nil?
343
+ defs[:label] = model.human_attribute_name(field) if defs[:label].nil?
344
+ return defs
345
+ end
346
+
347
+
348
+ # Returns true if model can be customized by current user at least by one field
349
+ def current_user_has_customizable_fields_for?(model, variant=nil)
350
+ return false if current_user.nil?
351
+ custom_table_customizable_fields_for(model, variant).count.positive?
352
+ end
353
+
354
+ def custom_table_data collection, variant=nil, **params
355
+
356
+ params[:collection] = collection
357
+ params[:variant] = variant
358
+ params[:paginate] = true if params[:paginate]!=false
359
+ params[:last_page] = true if params[:last_page]!=false
360
+ params[:namespace] = (controller.class.module_parent == Object) ? nil : controller.class.module_parent.to_s.underscore.to_sym
361
+ params[:force_edit_button] = false if params[:force_edit_button].nil?
362
+ params[:modal_edit] = true if params[:modal_edit].nil?
363
+
364
+ render "custom_table/table", params do
365
+ yield
366
+ end
367
+ end
368
+
369
+ # Data for updating values via turbo according object id and field name
370
+ def custom_table_row_data item, variant = nil, **params
371
+
372
+ params[:item] = item
373
+ params[:variant] = variant
374
+ params[:namespace] = (controller.class.module_parent == Object) ? nil : controller.class.module_parent.to_s.underscore.to_sym
375
+
376
+ render "custom_table/table_row_data", params do
377
+ yield
378
+ end
379
+ end
380
+
381
+ def custom_table_filter search_model, variant=nil, **params, &block
382
+
383
+ params[:search_model] = search_model
384
+ params[:variant] = variant
385
+ render "custom_table/filter", params do |f|
386
+ yield if !block.nil?
387
+ end
388
+ end
389
+
390
+ # Rounded
391
+ def amount_round(c)
392
+ content_tag(:span, amount_value(c, 0), "data-raw": (c.blank? ? nil : c.round))
393
+ end
394
+
395
+ # Base
396
+ def amount(c)
397
+ content_tag(:span, amount_value(c), "data-raw": (c.blank? ? nil : c.round(2)))
398
+ end
399
+
400
+ # Abstract
401
+ def amount_value(c, p = 2)
402
+ number_to_currency(c, precision: p, locale: :en, unit: "")
403
+ end
404
+
405
+ # Colored
406
+ def amount_color(c)
407
+ content_tag(:span, amount(c), class: ["amount", (c.to_f >= 0 ? "positive" : "negative")])
408
+ end
409
+
410
+ # Colored rounded
411
+ def amount_round_color(c)
412
+ content_tag(:span, amount_round(c), class: ["amount", (c.to_f >= 0 ? "positive" : "negative")])
413
+ end
414
+
415
+ def custom_table_download_button collection, **p
416
+ p[:collection] = collection
417
+ p[:downloads] ||= {}
418
+ p[:downloads].unshift({title: t("custom_table.download_as_csv"), href: params.permit!.merge({:format => :csv})}) if p[:csv]
419
+ render "custom_table/download", p
420
+ end
421
+
422
+ def custom_table_settings search_model, variant=nil, **params
423
+
424
+ params[:search_model] = search_model
425
+ params[:variant] = variant
426
+
427
+ render "custom_table/settings", params
428
+
429
+ end
430
+
431
+ def custom_table_settings_button search_model, variant=nil, size: "sm"
432
+ link_to custom_table.edit_setting_path(search_model.model_name, variant: variant), :class => "btn btn-outline-primary btn-#{size}", data: {"turbo-frame": "remote-modal"} do
433
+ custom_table_settings_icon
434
+ end
435
+ end
436
+
437
+ def custom_table_variants_for model
438
+ helper_name = "#{model.model_name.singular}_custom_table_variants"
439
+ return self.send(helper_name) if self.class.method_defined?(helper_name)
440
+ return []
441
+ end
442
+
443
+ def tree_opener item_id, has_children=false, expanded=false
444
+ content_tag :span, class: "tree-opener #{expanded ? 'opened' : ''}", data: {action: (has_children ? "click->table#toggle" : ""), "table-css-param": ".child-of-#{item_id}"} do
445
+ concat content_tag(:span, (has_children ? "▶" : "▷"), class: "closed")
446
+ concat content_tag(:span, "▼", class: "opened")
447
+ end
448
+ end
449
+
450
+ def custom_table_fields_totals fields:, totals:, item:, fields_totals:, variant:
451
+ fields.each do |field, defs|
452
+ if !totals.nil? && totals.has_key?(field) && totals[field].nil? # Auto-counting
453
+ fields_totals[field] = 0 if fields_totals[field].nil?
454
+ fields_totals[field] += raw_field_value_for(item, field, definitions: defs, variant: variant).to_f rescue 0
455
+ end
456
+ end
457
+ return fields_totals
458
+ end
459
+
460
+ def custom_table_batch_selector_check_box item, **params
461
+
462
+ # abort params.inspect
463
+
464
+ params[:param] = "#{item.model_name.plural}[]" if params[:param].nil?
465
+ params[:data] = {"toggle-target": "checkbox", "batch-actions-target": "checkbox", "action": "toggle#recalculateToggler batch-actions#refresh"}
466
+
467
+ check_box_tag params[:param], item.id, (!params[item.model_name.plural.to_sym].blank?) && (params[item.model_name.plural.to_sym].include?(item.id.to_s)), params
468
+ end
469
+
470
+ # Gets array of fields and results in object of available fields and definitions from fields from list
471
+ def custom_table_fields_list_to_definitions model, fields
472
+ d = custom_table_fields_definition_for(model)
473
+ out = {}
474
+ fields.each do |f|
475
+ found = d.find {|k, v| k == f }
476
+ out[f] = found[1] if !found.nil?
477
+ end
478
+ return out
479
+ end
480
+
481
+ end
482
+
483
+ end
@@ -0,0 +1,90 @@
1
+ module CustomTable
2
+
3
+ module FieldsetHelper
4
+
5
+ class FieldsetBuilder
6
+
7
+ def initialize(object, template, **params)
8
+ @object = object
9
+ @template = template
10
+ @params = params
11
+ end
12
+
13
+ def has_editable?
14
+ @template.respond_to?(:editable)
15
+ end
16
+
17
+ def field column, **params, &block
18
+
19
+ defs = custom_table_fields_definition_for_field(@object.class, column) rescue nil
20
+
21
+ params = {} if params.nil?
22
+ params = params.deep_merge(@params)
23
+ params[:editable] = true if params[:editable].nil?
24
+ params[:adaptive] = false if params[:adaptive].nil?
25
+
26
+ if params[:label].nil?
27
+ if !defs.nil? && !defs[:label].blank?
28
+ params[:label] = defs[:label].nil?
29
+ else
30
+ params[:label] = @object.class.human_attribute_name(column)
31
+ end
32
+ end
33
+
34
+ params[:template] = "field" if params[:template].nil?
35
+ params[:column] = column
36
+ params[:object] = @object
37
+
38
+ hint_key = "simple_form.hints.#{@object.model_name.singular}.#{column}"
39
+ if I18n.exists?(hint_key)
40
+ params[:hint] = I18n.t(hint_key)
41
+ end
42
+
43
+ @template.render "custom_table/#{params[:template]}", **params do
44
+ if params[:editable] && has_editable?
45
+ params[:editable_params] = {} if params[:editable_params].nil?
46
+ editable_field = params[:editable_params][:field] || column
47
+ @template.editable @object, editable_field, **params[:editable_params] do
48
+ if block_given?
49
+ yield
50
+ else
51
+ @template.field_value_for(@object, column)
52
+ end
53
+ end
54
+ else
55
+ if block_given?
56
+ yield
57
+ else
58
+ @template.field_value_for(@object, column)
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ def fieldset object=nil, **params, &block
68
+
69
+ builder = FieldsetBuilder.new(object, self, **params)
70
+ output = capture(builder, &block)
71
+
72
+ render "custom_table/fieldset" do
73
+ output
74
+ end
75
+
76
+ end
77
+
78
+ def field object, column, **params, &block
79
+
80
+ params[:template] = "field_plain"
81
+
82
+ builder = FieldsetBuilder.new(object, self, **params)
83
+
84
+ builder.field(column, **params, &block)
85
+
86
+ end
87
+
88
+ end
89
+
90
+ end
@@ -0,0 +1,42 @@
1
+ module CustomTable
2
+
3
+ module IconsHelper
4
+
5
+ def custom_table_download_icon
6
+ return custom_table_icon("fa fa-download") if CustomTable.configuration.icons_framework == :fa
7
+ return custom_table_icon("bi bi-download") if CustomTable.configuration.icons_framework == :bi
8
+ end
9
+
10
+ def custom_table_settings_icon
11
+ return custom_table_icon("fa fa-cog") if CustomTable.configuration.icons_framework == :fa
12
+ return custom_table_icon("bi bi-gear") if CustomTable.configuration.icons_framework == :bi
13
+
14
+ end
15
+
16
+ def custom_table_move_icon_class
17
+ return "fa fa-sort" if CustomTable.configuration.icons_framework == :fa
18
+ return "bi bi-arrow-down-up" if CustomTable.configuration.icons_framework == :bi
19
+ end
20
+
21
+ def custom_table_tree_child_icon_class
22
+ return "fa fa-arrow-right" if CustomTable.configuration.icons_framework == :fa
23
+ return "bi bi-arrow-return-right" if CustomTable.configuration.icons_framework == :bi
24
+ end
25
+
26
+ def custom_table_cancel_icon
27
+ return custom_table_icon("fa fa-ban") if CustomTable.configuration.icons_framework == :fa
28
+ return custom_table_icon("bi bi-slash-circle") if CustomTable.configuration.icons_framework == :bi
29
+ end
30
+
31
+ def custom_table_search_icon
32
+ return custom_table_icon("fa fa-search") if CustomTable.configuration.icons_framework == :fa
33
+ return custom_table_icon("bi bi-search") if CustomTable.configuration.icons_framework == :bi
34
+ end
35
+
36
+ def custom_table_icon c
37
+ return content_tag(:i, "", class: c)
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,52 @@
1
+ class DatePickerInput < SimpleForm::Inputs::StringInput
2
+ def input(wrapper_options)
3
+ set_html_options
4
+ set_value_html_option
5
+
6
+ classes = ["input-group"]
7
+
8
+ input_html_options[:data] ||= {}
9
+ input_html_options[:data]["controller"] = "flatpickr"
10
+
11
+ picker_options = (options[:picker] || {}).merge!(flatpickr_options)
12
+
13
+ picker_options.each do |k, v|
14
+ input_html_options[:data]["flatpickr-#{k}-value"] = v
15
+ end
16
+
17
+ input_html_options[:data]["flatpickr-locale-value"] = I18n.locale
18
+
19
+ classes.push("input-group-sm") if input_html_options[:class].include? "input-sm"
20
+
21
+ # template.content_tag :div, class: classes do
22
+ input = super(wrapper_options) # leave StringInput do the real rendering
23
+ # end
24
+ end
25
+
26
+ def input_html_classes
27
+ super.push '' # 'form-control'
28
+ end
29
+
30
+ private
31
+
32
+ def set_html_options
33
+ input_html_options[:type] = 'text'
34
+ input_html_options[:data] ||= {}
35
+ end
36
+
37
+ def set_value_html_option
38
+ # return unless value.present?
39
+ # input_html_options[:value] ||= I18n.localize(value, format: display_pattern)
40
+ end
41
+
42
+ def value
43
+ v = object.send(attribute_name) if object.respond_to?(attribute_name) || object.is_a?(Ransack::Search)
44
+ v = v.to_date if v.is_a?(String)
45
+ v
46
+ end
47
+
48
+ def flatpickr_options
49
+ {}
50
+ end
51
+
52
+ end