Skip to content

Commit

Permalink
Merge pull request #2803 from AlchemyCMS/attachment-select
Browse files Browse the repository at this point in the history
Add remote Attachment select to Link Dialog
  • Loading branch information
tvdeyen authored Apr 10, 2024
2 parents 17908f9 + 9e3b319 commit 099d488
Show file tree
Hide file tree
Showing 20 changed files with 223 additions and 47 deletions.
1 change: 1 addition & 0 deletions app/assets/stylesheets/alchemy/admin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
@import "alchemy/labels";
@import "alchemy/list_filter";
@import "alchemy/nodes";
@import "alchemy/attachment-select";
@import "alchemy/node-select";
@import "alchemy/notices";
@import "alchemy/page-select";
Expand Down
19 changes: 19 additions & 0 deletions app/assets/stylesheets/alchemy/attachment-select.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.attachment-select--attachment {
display: flex;
align-items: center;
height: 21px;

.icon {
margin: 0 $default-margin 0 0;

.select2-highlighted & {
fill: $white;
}
}
}

.attachment-select--attachment-name {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
6 changes: 6 additions & 0 deletions app/assets/stylesheets/alchemy/elements.scss
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,12 @@ alchemy-publish-element-button {
width: $icon-button-medium-width;
height: $icon-button-medium-height;
}

alchemy-link-buttons {
display: flex;
justify-content: space-between;
width: 38%;
}
}

.ingredient-editor.picture {
Expand Down
2 changes: 1 addition & 1 deletion app/assets/stylesheets/alchemy/node-select.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
margin: 0 8px 0 4px;

.select2-highlighted & {
color: $white;
fill: $white;
}
}
}
Expand Down
39 changes: 39 additions & 0 deletions app/components/alchemy/admin/attachment_select.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module Alchemy
module Admin
class AttachmentSelect < ViewComponent::Base
delegate :alchemy, to: :helpers

def initialize(attachment = nil, url: nil, placeholder: Alchemy.t("Please choose"), query_params: nil)
@attachment = attachment
@url = url
@placeholder = placeholder
@query_params = query_params
end

def call
content_tag("alchemy-attachment-select", content, attributes)
end

private

def attributes
options = {
"allow-clear": true,
placeholder: @placeholder,
url: @url || alchemy.api_attachments_path
}

if @query_params
options[:"query-params"] = @query_params.to_json
end

if @attachment
selection = ActiveModelSerializers::SerializableResource.new(@attachment)
options[:selection] = selection.to_json
end

options
end
end
end
end
13 changes: 5 additions & 8 deletions app/components/alchemy/admin/link_dialog/file_tab.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,15 @@ def message

private

def attachments
@_attachments ||= Attachment.all.collect { |f|
[f.name, alchemy.download_attachment_path(id: f.id, name: f.slug)]
}
def attachment
id = url&.match(/attachment\/(?<id>\d+)\/download/)&.captures
@_attachment ||= Alchemy::Attachment.find_by(id: id)
end

def attachment_select
label = label_tag("file_link", Alchemy.t(:file), class: "control-label")
select = select_tag "file_link",
options_for_select(attachments, is_selected? ? @url : nil),
prompt: Alchemy.t("Please choose"),
is: "alchemy-select"
input = text_field_tag("file_link", url, id: "file_link")
select = render Alchemy::Admin::AttachmentSelect.new(attachment).with_content(input)
content_tag("div", label + select, class: "input select")
end
end
Expand Down
44 changes: 44 additions & 0 deletions app/controllers/alchemy/api/attachments_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

module Alchemy
class Api::AttachmentsController < Api::BaseController
def index
authorize! :index, Attachment

@attachments = Attachment.all
@attachments = @attachments.ransack(params[:q]).result

if params[:page]
@attachments = @attachments.page(params[:page]).per(params[:per_page])
end

render json: @attachments, adapter: :json, root: "data", meta: meta_data
end

private

def meta_data
{
total_count: total_count_value,
per_page: per_page_value,
page: page_value
}
end

def total_count_value
params[:page] ? @attachments.total_count : @attachments.size
end

def per_page_value
if params[:page]
(params[:per_page] || Kaminari.config.default_per_page).to_i
else
@attachments.size
end
end

def page_value
params[:page] ? params[:page].to_i : 1
end
end
end
24 changes: 24 additions & 0 deletions app/javascript/alchemy_admin/components/attachment_select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { RemoteSelect } from "alchemy_admin/components/remote_select"

class AttachmentSelect extends RemoteSelect {
_renderResult(item) {
return this._renderListEntry(item)
}

/**
* html template for each list entry
* @param {object} page
* @returns {string}
* @private
*/
_renderListEntry(attachment) {
return `
<div class="attachment-select--attachment">
<alchemy-icon name="${attachment.icon_css_class}"></alchemy-icon>
<span class="attachment-select--attachment-name">${attachment.name}</span>
</div>
`
}
}

customElements.define("alchemy-attachment-select", AttachmentSelect)
1 change: 1 addition & 0 deletions app/javascript/alchemy_admin/components/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "alchemy_admin/components/attachment_select"
import "alchemy_admin/components/button"
import "alchemy_admin/components/char_counter"
import "alchemy_admin/components/clipboard_button"
Expand Down
1 change: 1 addition & 0 deletions app/javascript/alchemy_admin/components/link_buttons.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class LinkButtons extends HTMLElement {
this.linkTargetField.value = ""

this.linkButton.classList.remove("linked")
this.unlinkButton.linked = false

this.elementEditor.setDirty()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,18 @@ class LinkButton extends HTMLButtonElement {
}

setLink(link) {
this.classList.add("linked")
this.dispatchEvent(
new CustomEvent("alchemy:link", {
bubbles: true,
detail: link
})
)
if (link.url === "") {
this.classList.remove("linked")
this.dispatchEvent(new CustomEvent("alchemy:unlink", { bubbles: true }))
} else {
this.classList.add("linked")
this.dispatchEvent(
new CustomEvent("alchemy:link", {
bubbles: true,
detail: link
})
)
}
}

get linkUrl() {
Expand Down
8 changes: 0 additions & 8 deletions app/javascript/alchemy_admin/components/page_select.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import { RemoteSelect } from "alchemy_admin/components/remote_select"

class PageSelect extends RemoteSelect {
onChange(event) {
if (event.added) {
this.dispatchCustomEvent("PageSelect.ItemAdded", event.added)
} else {
this.dispatchCustomEvent("PageSelect.ItemRemoved")
}
}

get pageId() {
return this.selection ? JSON.parse(this.selection)["id"] : undefined
}
Expand Down
5 changes: 4 additions & 1 deletion app/javascript/alchemy_admin/components/remote_select.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export class RemoteSelect extends AlchemyHTMLElement {
* @param {Event} event
*/
onChange(event) {
// Left empty for sub classes to define this as needed.
this.dispatchCustomEvent("RemoteSelect.Change", {
removed: event.removed,
added: event.added
})
}

/**
Expand Down
22 changes: 15 additions & 7 deletions app/javascript/alchemy_admin/link_dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,21 @@ export class LinkDialog extends Alchemy.Dialog {
const internalForm = document.querySelector(
'[data-link-form-type="internal"]'
)
internalForm.addEventListener("Alchemy.PageSelect.ItemRemoved", (e) =>
this.#updatePage()
)
internalForm.addEventListener("Alchemy.PageSelect.ItemAdded", (e) =>
this.#updatePage(e.detail)
const attachmentSelect = document.querySelector(
'[data-link-form-type="file"] alchemy-attachment-select'
)

internalForm.addEventListener("Alchemy.RemoteSelect.Change", (e) => {
this.#updatePage(e.detail.added)
})

attachmentSelect.addEventListener("Alchemy.RemoteSelect.Change", (e) => {
const attachment = e.detail.added
document.getElementById("file_link").value = attachment
? attachment.url
: ""
})

document.querySelectorAll("[data-link-form-type]").forEach((form) => {
form.addEventListener("submit", (e) => {
e.preventDefault()
Expand All @@ -80,8 +88,8 @@ export class LinkDialog extends Alchemy.Dialog {
'[data-link-form-type="internal"] alchemy-dom-id-api-select'
)

internalLink.value = page != null ? page.url_path : undefined
domIdSelect.page = page != null ? page.id : undefined
internalLink.value = page ? page.url_path : ""
domIdSelect.page = page ? page.id : undefined
}

/**
Expand Down
8 changes: 8 additions & 0 deletions app/serializers/alchemy/attachment_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ class AttachmentSerializer < ActiveModel::Serializer
:file_name,
:file_mime_type,
:file_size,
:icon_css_class,
:tag_list,
:created_at,
:updated_at

attribute :url do
Alchemy::Engine.routes.url_helpers.download_attachment_path(
id: object.id,
name: object.file_name
)
end
end
end
21 changes: 11 additions & 10 deletions app/views/alchemy/admin/nodes/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,17 @@
const nodeUrl = document.getElementById("node_url")
const form = document.getElementById("node_form")

form.addEventListener("Alchemy.PageSelect.ItemAdded", (event) => {
const page = event.detail
nodeName.setAttribute("placeholder", page.name)
nodeUrl.value = page.url_path
nodeUrl.setAttribute("disabled", "disabled")
})
form.addEventListener("Alchemy.RemoteSelect.Change", (event) => {
const page = event.detail.added

form.addEventListener("Alchemy.PageSelect.ItemRemoved", (event) => {
nodeName.removeAttribute("placeholder")
nodeUrl.value = ""
nodeUrl.removeAttribute("disabled")
if (page) {
nodeName.setAttribute("placeholder", page.name)
nodeUrl.value = page.url_path
nodeUrl.setAttribute("disabled", "disabled")
} else {
nodeName.removeAttribute("placeholder")
nodeUrl.value = ""
nodeUrl.removeAttribute("disabled")
}
})
</script>
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
resources :elements, only: :show

namespace :api, defaults: {format: "json"} do
resources :attachments, only: [:index]
resources :ingredients, only: [:index]

resources :elements, only: [:index, :show]
Expand Down
7 changes: 4 additions & 3 deletions spec/components/alchemy/admin/link_dialog/file_tab_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,21 @@

context "file link" do
it "has a file select" do
expect(page).to have_selector("select[name=file_link] option")
expect(page).to have_selector("alchemy-attachment-select [name=file_link]")
end

context "tab selected" do
let(:is_selected) { true }

it "has a selected value" do
expect(page).to have_selector("select[name=file_link] option[selected='selected']")
expect(page).to have_selector("alchemy-attachment-select [value='#{url}']")
end
end

context "tab not selected" do
it "has a selected value" do
expect(page).to_not have_selector("select[name=file_link] option[selected='selected']")
expect(page).to have_selector("alchemy-attachment-select [value='#{url}']")
# expect(page).to_not have_selector("alchemy-attachment-select [name=file_link] option[selected='selected']")
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions spec/features/admin/link_overlay_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def fields

within "[name='overlay_tab_file_link']" do
expect(page).to have_selector("#file_link")
select2(file.name, from: "File")
select2_search(file.name, from: "File")
click_button "apply"
end

Expand All @@ -167,7 +167,7 @@ def fields

expect(page).to have_css("iframe#alchemy_preview_window", wait: 5)
within_frame "alchemy_preview_window" do
expect(page).to have_link("Link me", href: "/attachment/#{file.id}/download/#{file.name}")
expect(page).to have_link("Link me", href: "/attachment/#{file.id}/download/#{file.file_name}")
end
end
end
Expand Down
Loading

0 comments on commit 099d488

Please sign in to comment.