Skip to content

Instantly share code, notes, and snippets.

@tomasc
Last active September 18, 2018 10:24
Show Gist options
  • Save tomasc/b440f84bb5c680f71df40d2cee983ca2 to your computer and use it in GitHub Desktop.
Save tomasc/b440f84bb5c680f71df40d2cee983ca2 to your computer and use it in GitHub Desktop.
nested_fields
export default class IsSortable extends Modulor.Plugin
@defaults =
debug: false
name: 'Modulor__NestedFields__IsSortable'
@selector: "bems(modulor_nested_fields, is_sortable)"
on_init: ->
return if @sortable
Sortable = await `import('sortablejs' /* webpackChunkName: "sortablejs" */)`
@sortable = new Sortable(
@element,
animation: 150,
draggable: "bems(modulor_nested_fields, item)",
ghostClass: "bem(modulor_nested_fields, item, ghost)",
handle: "bems(modulor_nested_fields, item, handle)",
onAdd: (e) => @update_item_positions(),
onUpdate: (e) => @update_item_positions(),
onRemove: (e) => @update_item_positions(),
)
@$element.on "update_item_positions.#{@options.name}", (e) =>
e.stopPropagation()
@update_item_positions()
@update_item_positions()
on_destroy: ->
@sortable.destroy() if @sortable
get_items: -> @$element.find("bems(modulor_nested_fields, item)")
update_item_positions: ->
@get_items().each (i, el) =>
$(el).find('input[name*="position"]').val(i+1)
IsSortable.register()
# make sure positions are updated everytime items are added or removed
Modulor.MutationObserver.register(
"bems(modulor_nested_fields, item)",
((el) => $(el).trigger('update_item_positions')),
(=> $("bems(modulor_nested_fields, is_sortable)").trigger('update_item_positions'))
)
en:
modulor/nested_fields_builder:
links:
add: 'Add %{model_name}'
remove: 'Remove'
# FORM
# = form.nested_fields_for :locations
# = form.nested_fields_for :locations, filtered_locations
# = form.nested_fields_for :locations, nil, sortable: true
#
# FIELDS
# partial at `locations/fields`
module ActionView
module Helpers
class FormBuilder
def nested_fields_for(record_name, record_object = nil, options = {}, &block)
Modulor::NestedFieldsBuilder.new(self, @template, record_name, record_object, options).nested_fields_for(&block)
end
end
end
end
module Modulor
class NestedFieldsBuilder < Struct.new(:builder, :template, :record_name, :record_object, :options)
CHILD_INDEX_STRING = '__INDEX_PLACEHOLDER__'.freeze
delegate :object,
:object_name,
:simple_fields_for,
to: :builder
delegate :content_tag,
:hidden_field_tag,
:link_to,
:render,
to: :template
def initialize(builder, template, record_name, record_object = nil, options = {})
super(builder, template, record_name, record_object, options)
end
def nested_fields_for
dom_class = Bem.bem([:modulor_nested_fields, (:is_sortable if is_sortable?)])
content_tag(:div, class: dom_class) do
[
nested_fields_title,
simple_fields_for(record_name, record_object, options) do |fields|
dom_class = [Bem.bem(:modulor_nested_fields, :item), Bem.bem(relation.klass)]
dom_data = { id: fields.object.id.to_s }
content_tag(:div, class: dom_class, data: dom_data) do
nested_fields_item_handle.to_s.html_safe +
render(partial_path, fields: fields).html_safe +
link_to_remove(fields)
end.html_safe
end,
nested_fields_links
].reject(&:blank?).join.html_safe
end.html_safe
end
private
def is_sortable?
options[:sortable] == true
end
def partial_path
object.to_view_path("#{record_name}/fields")
end
def relation
object.reflect_on_association(record_name)
end
def nested_fields_title
dom_class = Bem.bem(:modulor_nested_fields, :title)
title = relation.klass.model_name.human.pluralize
content_tag(:div, title, class: dom_class).html_safe
end
def nested_fields_links
dom_class = Bem.bem(:modulor_nested_fields, :links)
content_tag(:div, link_to_add, class: dom_class).html_safe
end
def link_to_add
label = options.fetch(:label_add, ::I18n.t(:add, scope: %i(modulor/nested_fields_builder links), model_name: relation.klass.model_name.human))
dom_class = Bem.bem([:modulor_link, :nested_fields, :add])
dom_data = { template: CGI.escapeHTML(nested_fields_template).html_safe, turbolinks: 'false' }
link_to(label, '#', class: dom_class, data: dom_data).html_safe
end
def nested_fields_item_handle
return unless is_sortable?
dom_class = Bem.bem(:modulor_nested_fields, :item, :handle)
content_tag(:div, nil, class: dom_class).html_safe
end
def nested_fields_template
dom_class = [Bem.bem(:modulor_nested_fields, :item), Bem.bem(relation.klass)]
content_tag(:div, class: dom_class) do
nested_fields_template_string
end.html_safe
end
def nested_fields_template_string
simple_fields_for(record_name, relation.klass.new, child_index: CHILD_INDEX_STRING) do |fields|
nested_fields_item_handle.to_s.html_safe +
render(partial_path, fields: fields).html_safe +
link_to_remove(fields)
end.html_safe
end
def destroy_field_tag(fields)
return if fields.object.new_record?
hidden_field_tag("#{fields.object_name}[_destroy]", fields.object._destroy).html_safe
end
def link_to_remove(fields, options = {})
label = options.fetch(:label, ::I18n.t(:remove, scope: %i(modulor/nested_fields_builder links)))
dom_class = Bem.bem([:modulor_link, :nested_fields, :remove])
dom_data = { turbolinks: 'false' }
[
destroy_field_tag(fields),
link_to(label, '#', class: dom_class, data: dom_data)
].reject(&:blank?).join.html_safe
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment