f_components 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|