Module: VenusMediaLibrary::PickerHelper

Defined in:
app/helpers/venus_media_library/picker_helper.rb

Overview

Host-facing helper. Included into the host app's views by the engine (see VenusMediaLibrary::Engine), so apps can drop the picker next to any URL field.

<%= form_with model: @page do |f| %>
<%= media_picker_field f, :og_image %>
<% end %>

Renders a text input (the field the host already had) plus a "Choose from library" button wired to open the modal. Selecting an image writes its URL into the input; a hidden <field>_signed_id input also receives the blob's signed id so the host can attach it if desired.

Instance Method Summary collapse

Instance Method Details

#media_attach_field(form, attachment, **opts) ⇒ Object

Attach-mode picker. Use this for an Active Storage attachment (has_one_attached) rather than a URL column: the picker writes the chosen blob's signed_id into a hidden field NAMED FOR THE ATTACHMENT (e.g. product), so Rails attaches that blob on save.

<%= form_with model: @product do |f| %>
<%= media_attach_field f, :cover %>
<% end %>

The visible input is a read-only preview (it is NOT submitted). The hidden attachment field is disabled until an image is picked, because submitting an empty string for a has_one_attached would DETACH the current file; the JS sets the signed_id and enables the field on selection.

form - a form builder bound to the record attachment - the has_one_attached name (e.g. :cover) opts:

:label       - button label (default "Choose from library")
:placeholder - preview input placeholder
:input_html  - extra HTML options merged into the preview input
:class       - wrapper CSS class


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'app/helpers/venus_media_library/picker_helper.rb', line 77

def media_attach_field(form, attachment, **opts)
  input_id = "#{form.object_name}_#{attachment}".parameterize.tr("-", "_")
  label    = opts.fetch(:label, "Choose from library")
  wrapper  = opts.fetch(:class, "ml-field")

  preview_html = {
    id:    input_id,
    class: "ml-field__input",
    type:  "text",
    readonly: true,
    value: ml_attachment_preview_value(form.object, attachment),
    placeholder: opts[:placeholder]
  }.merge(opts[:input_html] || {})

  picker_src = venus_media_library.picker_path(target: input_id)

  (:div, class: wrapper, data: { ml_field: input_id }) do
    parts = []

    # Read-only preview of the current/chosen image. No name -> not submitted.
    parts << tag.input(**preview_html)

    # Hidden field named for the attachment. Disabled (so a blank value can't
    # detach on save) until the JS writes a signed_id and enables it.
    parts << form.hidden_field(attachment,
      value: "",
      disabled: true,
      id: "#{input_id}_signed_id",
      data: { ml_signed_id_for: input_id })

    parts << button_tag(label,
      type: "button",
      class: "ml-btn ml-btn--primary ml-field__button",
      data: { ml_open: true, ml_target: input_id, ml_src: picker_src })

    safe_join(parts)
  end + venus_media_library_modal_shell
end

#media_picker_field(form, field, **opts) ⇒ Object

form - a form builder (form_with / form_for) field - the attribute holding the image URL (e.g. :og_image) opts:

:label            - button label (default "Choose from library")
:placeholder      - text input placeholder
:signed_id_field  - name for the hidden signed-id input
                  (default "<field>_signed_id"); pass false to skip
:input_html       - extra HTML options merged into the text input
:class            - wrapper CSS class


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'app/helpers/venus_media_library/picker_helper.rb', line 23

def media_picker_field(form, field, **opts)
  input_id  = "#{form.object_name}_#{field}".parameterize.tr("-", "_")
  label     = opts.fetch(:label, "Choose from library")
  wrapper   = opts.fetch(:class, "ml-field")
  input_html = {
    id:    input_id,
    class: "ml-field__input",
    placeholder: opts[:placeholder]
  }.merge(opts[:input_html] || {})

  signed_id_field = opts.fetch(:signed_id_field, "#{field}_signed_id")

  # Endpoint the JS uses to load the picker frame for this field.
  picker_src = venus_media_library.picker_path(target: input_id)

  (:div, class: wrapper, data: { ml_field: input_id }) do
    parts = []
    parts << form.text_field(field, input_html)

    if signed_id_field
      hidden_id = "#{input_id}_signed_id"
      parts << form.hidden_field(signed_id_field, id: hidden_id, data: { ml_signed_id_for: input_id })
    end

    parts << button_tag(label,
      type: "button",
      class: "ml-btn ml-btn--primary ml-field__button",
      data: { ml_open: true, ml_target: input_id, ml_src: picker_src })

    safe_join(parts)
  end + venus_media_library_modal_shell
end

#ml_attachment_preview_value(record, attachment) ⇒ Object

Best-effort URL for the currently attached file, used to seed the preview. Rescues to the filename (or blank) so a missing host/route never breaks the form.



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'app/helpers/venus_media_library/picker_helper.rb', line 118

def ml_attachment_preview_value(record, attachment)
  attached = record.respond_to?(attachment) && record.public_send(attachment)
  return "" unless attached && attached.respond_to?(:attached?) && attached.attached?

  if VenusMediaLibrary.configuration.url_type == :proxy
    rails_storage_proxy_url(attached)
  else
    rails_blob_url(attached)
  end
rescue StandardError
  begin
    attached && attached.attached? ? attached.filename.to_s : ""
  rescue StandardError
    ""
  end
end

#venus_media_library_modal_shellObject

Renders the shared modal shell once per page. The picker frame is lazy loaded into it when a button is clicked.



137
138
139
140
141
142
# File 'app/helpers/venus_media_library/picker_helper.rb', line 137

def venus_media_library_modal_shell
  return "".html_safe if @_venus_media_library_modal_rendered

  @_venus_media_library_modal_rendered = true
  render partial: "venus_media_library/pickers/modal"
end