f_components 0.2.1
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 +7 -0
- data/README.md +144 -0
- data/Rakefile +20 -0
- data/app/components/f_components/avatar/component.html.erb +1 -0
- data/app/components/f_components/avatar/component.rb +30 -0
- data/app/components/f_components/avatar/component.scss +3 -0
- data/app/components/f_components/base.rb +14 -0
- data/app/components/f_components/button/component.html.erb +5 -0
- data/app/components/f_components/button/component.rb +68 -0
- data/app/components/f_components/button/component.scss +9 -0
- data/app/components/f_components/collapsible/component.html.erb +6 -0
- data/app/components/f_components/collapsible/component.rb +15 -0
- data/app/components/f_components/collapsible/component.scss +19 -0
- data/app/components/f_components/dropdown/component.html.erb +10 -0
- data/app/components/f_components/dropdown/component.rb +19 -0
- data/app/components/f_components/dropdown/component.scss +10 -0
- data/app/components/f_components/form_field/component.html.erb +25 -0
- data/app/components/f_components/form_field/component.rb +124 -0
- data/app/components/f_components/resource_table/component.html.erb +17 -0
- data/app/components/f_components/resource_table/component.rb +139 -0
- data/app/components/f_components/table/component.html.erb +23 -0
- data/app/components/f_components/table/component.rb +88 -0
- data/app/components/f_components/table/component_controller.js +84 -0
- data/app/components/f_components/table/desktop/component.html.erb +25 -0
- data/app/components/f_components/table/desktop/component.rb +71 -0
- data/app/components/f_components/table/mobile/component.html.erb +8 -0
- data/app/components/f_components/table/mobile/component.rb +102 -0
- data/app/frontend/f_components/stylesheets/blocks/_button.scss +189 -0
- data/app/frontend/f_components/stylesheets/f_components.scss +12 -0
- data/app/frontend/f_components/stylesheets/variables/_breakpoints.scss +3 -0
- data/app/frontend/f_components/stylesheets/variables/_colors.scss +19 -0
- data/app/helpers/f_components/application_helper.rb +13 -0
- data/app/helpers/f_components/components_helper.rb +33 -0
- data/app/helpers/f_components/icons_helper.rb +34 -0
- data/config/webpack/development.js +5 -0
- data/config/webpack/environment.js +3 -0
- data/config/webpack/production.js +7 -0
- data/config/webpacker.yml +89 -0
- data/lib/f_components/engine.rb +28 -0
- data/lib/f_components/railtie.rb +12 -0
- data/lib/f_components/version.rb +5 -0
- data/lib/f_components.rb +18 -0
- metadata +158 -0
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FComponents
|
4
|
+
module ResourceTable
|
5
|
+
class Component < Base
|
6
|
+
attributes :attributes, :resources, resource_class: nil, resource_path: nil,
|
7
|
+
class: nil, selectable: {}, actions: true
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def table_header
|
12
|
+
tag.thead do
|
13
|
+
tag.tr class: 'text-left border-b-2 border-gray-lt' do
|
14
|
+
row = table_headers
|
15
|
+
row.prepend(tag.th(class: 'py-3 px-7 w-2') { nil }) if selectable.present?
|
16
|
+
row.append(tag.th(class: 'py-4 px-7')) if actions
|
17
|
+
|
18
|
+
safe_join(row)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def table_headers
|
24
|
+
attributes.map do |attribute|
|
25
|
+
tag.th(class: 'py-4 px-7') { attribute_name(attribute) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def table_row(resource, class:)
|
30
|
+
tag.tr class: binding.local_variable_get(:class) do
|
31
|
+
row = []
|
32
|
+
row.prepend(tag.td(class: 'py-3 px-7') { form_check_box(resource) }) if selectable.present?
|
33
|
+
row.append(table_rows(resource))
|
34
|
+
row.append(link_to_resource(resource)) if actions
|
35
|
+
|
36
|
+
safe_join(row)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def table_rows(resource)
|
41
|
+
attributes.map { |attribute| tag.td(class: 'py-3 px-7') { row_value(resource, attribute) } }
|
42
|
+
end
|
43
|
+
|
44
|
+
def mobile_header(resource, class:, &block)
|
45
|
+
collapsible_title = row_value(resource, attributes.first, class: 'font-bold text-sm')
|
46
|
+
|
47
|
+
if selectable.present?
|
48
|
+
check_box = form_check_box(resource)
|
49
|
+
collapsible_title = tag.div(class: 'w-full flex space-x-4 items-center') { check_box + collapsible_title }
|
50
|
+
end
|
51
|
+
|
52
|
+
fcomponent(:collapsible, summary: collapsible_title, class: binding.local_variable_get(:class), &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
def mobile_rows(resource, class:)
|
56
|
+
tag.div class: binding.local_variable_get(:class) do
|
57
|
+
safe_join(other_attributes.map { |attribute| row(resource, attribute) })
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def link_to_resource(resource, display_as: :link)
|
62
|
+
path = resource_path.present? ? resource_path.(resource) : resource
|
63
|
+
return tag.td(class: 'py-3 px-7') { link_to('Detalhes', path) } if display_as == :link
|
64
|
+
|
65
|
+
fcomponent :button, 'Detalhes', path, type: :secondary, mods: [:sm], class: 'mt-7 mx-auto'
|
66
|
+
end
|
67
|
+
|
68
|
+
def other_attributes
|
69
|
+
Array(attributes[1..])
|
70
|
+
end
|
71
|
+
|
72
|
+
def row(resource, attribute)
|
73
|
+
tag.div(class: 'flex items-center text-sm') do
|
74
|
+
row_label(attribute) + row_value(resource, attribute)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def row_label(attribute)
|
79
|
+
tag.strong(class: 'w-20 min-w-20 mr-8') { attribute_name(attribute) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def row_value(resource, attribute, class: nil)
|
83
|
+
if (attr_value = attribute_value(resource, attribute))
|
84
|
+
tag.p(class: "max-w-5/6 #{binding.local_variable_get(:class)}") do
|
85
|
+
format_attribute_value(attribute, attr_value)
|
86
|
+
end
|
87
|
+
else
|
88
|
+
attribute_fallback
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def format_attribute_value(attribute, value)
|
93
|
+
return value.to_s unless resource_class.defined_enums.has_key?(attribute.to_s)
|
94
|
+
|
95
|
+
resource_class.human_enum_name(attribute.to_s, value)
|
96
|
+
end
|
97
|
+
|
98
|
+
def attribute_name(attribute)
|
99
|
+
resource_class.human_attribute_name(attribute)
|
100
|
+
end
|
101
|
+
|
102
|
+
def attribute_value(resource, attribute)
|
103
|
+
resource.public_send(attribute)
|
104
|
+
end
|
105
|
+
|
106
|
+
def attribute_fallback
|
107
|
+
@attribute_fallback ||= tag.p(class: 'text-red') { 'Dados insuficientes' }
|
108
|
+
end
|
109
|
+
|
110
|
+
def resource_class
|
111
|
+
@resource_class ||=
|
112
|
+
case resources.class.to_s
|
113
|
+
when 'ActiveRecord::Relation'
|
114
|
+
resources.model
|
115
|
+
when 'Draper::CollectionDecorator'
|
116
|
+
resources.object.model
|
117
|
+
else
|
118
|
+
resources.first.class
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def table_layout
|
123
|
+
selectable.present? ? 'md:table md:table-fixed' : 'md:table'
|
124
|
+
end
|
125
|
+
|
126
|
+
def resources_class_name
|
127
|
+
resource_class.to_s.parameterize(separator: '_')
|
128
|
+
end
|
129
|
+
|
130
|
+
def form
|
131
|
+
selectable[:form]
|
132
|
+
end
|
133
|
+
|
134
|
+
def form_check_box(resource)
|
135
|
+
form.check_box(resources_class_name, { multiple: true }, resource.id, nil)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<div data-controller="f-table-component">
|
2
|
+
<%=
|
3
|
+
desktop_table(
|
4
|
+
columns: columns,
|
5
|
+
rows: rows,
|
6
|
+
actions: @actions,
|
7
|
+
check_boxes: @check_boxes,
|
8
|
+
**@options
|
9
|
+
)
|
10
|
+
%>
|
11
|
+
|
12
|
+
<%=
|
13
|
+
mobile_table(
|
14
|
+
columns: columns,
|
15
|
+
rows: rows,
|
16
|
+
main_column: @main_column,
|
17
|
+
actions: @actions,
|
18
|
+
check_boxes: @check_boxes,
|
19
|
+
resources: @resources,
|
20
|
+
**@options
|
21
|
+
)
|
22
|
+
%>
|
23
|
+
</div>
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FComponents
|
4
|
+
module Table
|
5
|
+
class Component < Base
|
6
|
+
renders_one :desktop_table, Table::Desktop::Component
|
7
|
+
renders_one :mobile_table, Table::Mobile::Component
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@options = options
|
11
|
+
@values = {}
|
12
|
+
@actions = []
|
13
|
+
@check_boxes = []
|
14
|
+
@main_column = nil
|
15
|
+
@current_resource = nil
|
16
|
+
@current_resource_index = 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.for(resources, **options, &block)
|
20
|
+
table = new(options)
|
21
|
+
table.build(resources, &block)
|
22
|
+
|
23
|
+
table
|
24
|
+
end
|
25
|
+
|
26
|
+
def column(name, id: nil, main: false)
|
27
|
+
if main
|
28
|
+
@main_column = main.is_a?(Proc) ? main : name
|
29
|
+
end
|
30
|
+
|
31
|
+
@values[name] ||= []
|
32
|
+
|
33
|
+
return if @current_resource.blank?
|
34
|
+
|
35
|
+
@values[name] << { id: id, column_index: @current_resource_index, cell_content: yield(@current_resource).to_s }
|
36
|
+
end
|
37
|
+
|
38
|
+
def check_box(name:, value:, checked: false, **options)
|
39
|
+
value = value.(@current_resource) if value.respond_to? :call
|
40
|
+
checked = checked.(@current_resource) if checked.respond_to? :call
|
41
|
+
|
42
|
+
parsed_options = options
|
43
|
+
|
44
|
+
options.each do |k, v|
|
45
|
+
parsed_options[k] = v.(@current_resource) if v.respond_to? :call
|
46
|
+
end
|
47
|
+
|
48
|
+
parsed_options[:id] ||= "#{sanitize_to_id(name)}#{value}"
|
49
|
+
|
50
|
+
@check_boxes << check_box_tag(name, value, checked, **parsed_options)
|
51
|
+
end
|
52
|
+
|
53
|
+
def action
|
54
|
+
action = yield(@current_resource)
|
55
|
+
|
56
|
+
@actions[@current_resource_index] ||= []
|
57
|
+
@actions[@current_resource_index] << action
|
58
|
+
end
|
59
|
+
|
60
|
+
def build(resources)
|
61
|
+
@resources = resources.presence || [nil]
|
62
|
+
@resources.each_with_index do |resource, i|
|
63
|
+
@current_resource = resource
|
64
|
+
@current_resource_index = i
|
65
|
+
|
66
|
+
yield(self, @current_resource)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def columns
|
73
|
+
@values.keys
|
74
|
+
end
|
75
|
+
|
76
|
+
def rows
|
77
|
+
@values.values.transpose
|
78
|
+
end
|
79
|
+
|
80
|
+
# This was copied from Rails, since it's not a public API
|
81
|
+
# Source:
|
82
|
+
# https://github.com/rails/rails/blob/d305be07428a8e86e2736b57f839680c9e970293/actionview/lib/action_view/helpers/form_tag_helper.rb#L931
|
83
|
+
def sanitize_to_id(name)
|
84
|
+
name.to_s.delete(']').tr('^-a-zA-Z0-9:.', '_')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import { Controller as BaseController } from '@hotwired/stimulus';
|
2
|
+
import { findAll } from '@fretadao/f-js-dom';
|
3
|
+
|
4
|
+
export default class extends BaseController {
|
5
|
+
static targets = ['desktopTable', 'mobileTable'];
|
6
|
+
|
7
|
+
connect() {
|
8
|
+
this.toggleDuplicatesHandler = this.toggleDuplicates.bind(this);
|
9
|
+
|
10
|
+
this.checkboxes().forEach(checkbox => {
|
11
|
+
checkbox.addEventListener('change', this.toggleDuplicatesHandler);
|
12
|
+
});
|
13
|
+
|
14
|
+
|
15
|
+
this.syncInputHandler = this.syncInput.bind(this)
|
16
|
+
|
17
|
+
this.inputs().forEach(input => {
|
18
|
+
input.addEventListener('input', this.syncInputHandler);
|
19
|
+
})
|
20
|
+
|
21
|
+
document.dispatchEvent(this.#loadedEvent());
|
22
|
+
}
|
23
|
+
|
24
|
+
disconnect() {
|
25
|
+
this.checkboxes().forEach(checkbox => {
|
26
|
+
checkbox.removeEventListener('change', this.toggleDuplicatesHandler);
|
27
|
+
});
|
28
|
+
|
29
|
+
this.inputs().forEach(input => {
|
30
|
+
input.removeEventListener('change', this.syncInputHandler);
|
31
|
+
})
|
32
|
+
}
|
33
|
+
|
34
|
+
toggleDuplicates(event) {
|
35
|
+
const checkbox = event.currentTarget;
|
36
|
+
|
37
|
+
this.checkboxes({ id: checkbox.id, value: checkbox.value }).forEach(dup => {
|
38
|
+
dup.checked = checkbox.checked;
|
39
|
+
});
|
40
|
+
|
41
|
+
this.element.dispatchEvent(new CustomEvent('checkboxesChanged', { bubbles: true }));
|
42
|
+
}
|
43
|
+
|
44
|
+
syncInput(event) {
|
45
|
+
const input = event.currentTarget;
|
46
|
+
|
47
|
+
this.inputs(input.id).forEach(dup => {
|
48
|
+
dup.value = input.value;
|
49
|
+
});
|
50
|
+
|
51
|
+
this.element.dispatchEvent(new CustomEvent('inputsChanged', { bubbles: true }));
|
52
|
+
}
|
53
|
+
|
54
|
+
checkboxes({ id, value } = {}) {
|
55
|
+
let query = 'input[type=checkbox]';
|
56
|
+
|
57
|
+
if(!!id) {
|
58
|
+
query = `#${id}`;
|
59
|
+
} else if (!!value) {
|
60
|
+
query += `[value="${value}"]`;
|
61
|
+
}
|
62
|
+
|
63
|
+
return findAll(query, this.element);
|
64
|
+
}
|
65
|
+
|
66
|
+
inputs(id = null) {
|
67
|
+
let query = 'input:not([type=checkbox])';
|
68
|
+
|
69
|
+
if(!!id) {
|
70
|
+
query = `#${id}`;
|
71
|
+
}
|
72
|
+
|
73
|
+
return findAll(query, this.element);
|
74
|
+
}
|
75
|
+
|
76
|
+
#loadedEvent() {
|
77
|
+
return new CustomEvent(
|
78
|
+
'f-components-table:loaded',
|
79
|
+
{
|
80
|
+
detail: { desktopTable: this.desktopTableTarget, mobileTable: this.mobileTableTarget }
|
81
|
+
}
|
82
|
+
);
|
83
|
+
}
|
84
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<%= tag.table(id: @id, class: "hidden md:w-full md:table md:text-sm #{@class}", **@options) do %>
|
2
|
+
<thead>
|
3
|
+
<tr class="text-left border-b-2 border-gray-lt">
|
4
|
+
<% @columns.each do |header| %>
|
5
|
+
<th class='px-4 py-3'><%= header %></th>
|
6
|
+
<% end %>
|
7
|
+
</tr>
|
8
|
+
</thead>
|
9
|
+
|
10
|
+
<tbody>
|
11
|
+
<% @rows.each_with_index do |row, i| %>
|
12
|
+
<tr class='even:bg-gray-ltr'>
|
13
|
+
<%= check_box_for(resource_index: i) %>
|
14
|
+
|
15
|
+
<% row.each do |cell| %>
|
16
|
+
<td class="px-4 py-3">
|
17
|
+
<%= format_cell(cell) %>
|
18
|
+
</td>
|
19
|
+
<% end %>
|
20
|
+
|
21
|
+
<%= actions(resource_index: i) %>
|
22
|
+
</tr>
|
23
|
+
<% end %>
|
24
|
+
</tbody>
|
25
|
+
<% end %>
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FComponents
|
4
|
+
module Table
|
5
|
+
module Desktop
|
6
|
+
class Component < Base
|
7
|
+
def initialize(**options)
|
8
|
+
@rows = options.delete(:rows)
|
9
|
+
@actions = options.delete(:actions)
|
10
|
+
@check_boxes = options.delete(:check_boxes)
|
11
|
+
@columns = options.delete(:columns)
|
12
|
+
@columns.prepend('') if @check_boxes.present?
|
13
|
+
@columns.push('') if @actions.present?
|
14
|
+
@id = ['desktop-table', options.delete(:id_suffix)].filter_map(&:presence).join('-')
|
15
|
+
@class = options.delete(:class)
|
16
|
+
add_target(options)
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
def check_box_for(resource_index:)
|
21
|
+
return if @check_boxes.empty?
|
22
|
+
|
23
|
+
tag.td(class: 'py-3 text-center') { @check_boxes[resource_index] }
|
24
|
+
end
|
25
|
+
|
26
|
+
def actions(resource_index:)
|
27
|
+
return if @actions.blank?
|
28
|
+
|
29
|
+
resource_actions = @actions[resource_index]
|
30
|
+
|
31
|
+
tag.td(class: 'py-3 px-4') do
|
32
|
+
actions_for_resource(resource_actions)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def actions_for_resource(resource_actions)
|
37
|
+
fcomponent :dropdown, label: 'Opções', padding: false, icon: 'plus', class: 'w-fit mx-auto' do
|
38
|
+
safe_join(
|
39
|
+
resource_actions.map do |action|
|
40
|
+
tag.div(class: 'text-sm py-3 px-5 even:bg-gray-lt') do
|
41
|
+
action
|
42
|
+
end
|
43
|
+
end
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def format_cell(value)
|
49
|
+
cell_content = value.is_a?(Hash) ? value[:cell_content] : value
|
50
|
+
return cell_content if cell_content.present? && html?(cell_content)
|
51
|
+
|
52
|
+
cell_id = value[:id].present? ? "#{value[:id]}-desktop-#{value[:column_index]}" : nil
|
53
|
+
|
54
|
+
tag.p(id: cell_id, class: 'max-w-5/6') { cell_content }
|
55
|
+
end
|
56
|
+
|
57
|
+
def html?(test_target)
|
58
|
+
Nokogiri::XML.parse(test_target.to_s).errors.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_target(options)
|
62
|
+
options ||= {}
|
63
|
+
options[:data] ||= {}
|
64
|
+
options[:data][:f_table_component_target] = 'desktopTable'
|
65
|
+
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FComponents
|
4
|
+
module Table
|
5
|
+
module Mobile
|
6
|
+
class Component < Base
|
7
|
+
def initialize(**options)
|
8
|
+
@rows = options.delete(:rows)
|
9
|
+
@actions = options.delete(:actions)
|
10
|
+
@main_column = options.delete(:main_column)
|
11
|
+
@check_boxes = options.delete(:check_boxes)
|
12
|
+
@columns = options.delete(:columns)
|
13
|
+
@id = ['mobile-table', options.delete(:id_suffix)].filter_map(&:presence).join('-')
|
14
|
+
@class = options.delete(:class)
|
15
|
+
@resources = options.delete(:resources)
|
16
|
+
add_target(options)
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
def table_head(resource_index:, &block)
|
21
|
+
current_resource = @resources[resource_index]
|
22
|
+
|
23
|
+
if @main_column.is_a?(Proc) && current_resource.present?
|
24
|
+
main_header = @main_column.call(current_resource)
|
25
|
+
else
|
26
|
+
main_header_index = @columns.index(@main_column) or invalid_column_error!
|
27
|
+
main_header = tag.strong(class: 'text-sm') do
|
28
|
+
value = @rows[resource_index][main_header_index]
|
29
|
+
|
30
|
+
value.is_a?(Hash) ? value[:cell_content] : value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
title = if @check_boxes.present?
|
35
|
+
@check_boxes[resource_index] + main_header
|
36
|
+
else
|
37
|
+
main_header
|
38
|
+
end
|
39
|
+
|
40
|
+
fcomponent(:collapsible, summary: title, class: 'bg-white border-b border-gray-lt', &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def rows
|
44
|
+
@rows.map do |row|
|
45
|
+
row.map { |cell| format_cell(cell) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def table_body(row)
|
50
|
+
tag.div class: 'flex flex-col space-y-5 mt-5' do
|
51
|
+
safe_join(
|
52
|
+
row.each_with_index.map do |value, position|
|
53
|
+
tag.div(class: 'flex items-center text-sm', data: { mobile_col: true }) do
|
54
|
+
tag.strong(class: 'w-20 min-w-20 mr-8') { @columns[position] } + format_cell(value)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def actions(resource_index:)
|
62
|
+
return if @actions.blank?
|
63
|
+
|
64
|
+
fcomponent :dropdown, label: 'Opções', padding: false, icon: 'plus', class: 'mt-5' do
|
65
|
+
safe_join(
|
66
|
+
@actions[resource_index].map do |action|
|
67
|
+
tag.div(class: 'text-sm py-3 px-5 even:bg-gray-lt') do
|
68
|
+
action
|
69
|
+
end
|
70
|
+
end
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def format_cell(value)
|
76
|
+
cell_content = value.is_a?(Hash) ? value[:cell_content] : value
|
77
|
+
return cell_content if cell_content.present? && html?(cell_content)
|
78
|
+
|
79
|
+
cell_id = value[:id].present? ? "#{value[:id]}-mobile-#{value[:column_index]}" : nil
|
80
|
+
|
81
|
+
tag.p(id: cell_id, class: 'max-w-5/6') { cell_content }
|
82
|
+
end
|
83
|
+
|
84
|
+
def html?(test_target)
|
85
|
+
Nokogiri::XML.parse(test_target.to_s).errors.empty?
|
86
|
+
end
|
87
|
+
|
88
|
+
def invalid_column_error!
|
89
|
+
raise "Main column '#{@main_column}' not found in #{@columns}. Maybe you forgot to specify a main column?"
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_target(options)
|
93
|
+
options ||= {}
|
94
|
+
options[:data] ||= {}
|
95
|
+
options[:data][:f_table_component_target] = 'mobileTable'
|
96
|
+
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|