Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to add anchors to ingredients #2395

Merged
merged 10 commits into from
Dec 16, 2022
24 changes: 13 additions & 11 deletions app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class window.Alchemy.LinkDialog extends Alchemy.Dialog
(data) =>
page = data.pages[0]
if page
@initElementSelect(page.id)
@initDomIdSelect(page.id)
callback
id: page.url_path
name: page.name
Expand All @@ -116,16 +116,19 @@ class window.Alchemy.LinkDialog extends Alchemy.Dialog
@$element_anchor.select2('destroy').prop('disabled', true)
else
@$element_anchor.val('')
@initElementSelect(event.added.page_id)
@initDomIdSelect(event.added.page_id)

# Initializes the select2 based elements select
# Initializes the select2 based dom id select
# reveals after a page has been selected
initElementSelect: (page_id) ->
$.get Alchemy.routes.api_elements_path, page_id: page_id, (data) =>
initDomIdSelect: (page_id) ->
$.get Alchemy.routes.api_ingredients_path, page_id: page_id, (data) =>
dom_ids = data.ingredients.filter (ingredient) ->
ingredient.data?.dom_id
.map (ingredient) ->
id: ingredient.data.dom_id
text: "##{ingredient.data.dom_id}"
@$element_anchor.prop('disabled', false).removeAttr('placeholder').select2
data: [ id: '', text: Alchemy.t('None') ].concat data.elements.map (element) ->
id: element.dom_id
text: element.display_name
data: [ id: '', text: Alchemy.t('None') ].concat(dom_ids)

# Creates a link if no validation errors are present.
# Otherwise shows an error notice.
Expand Down Expand Up @@ -238,11 +241,10 @@ class window.Alchemy.LinkDialog extends Alchemy.Dialog
# Populates the internal anchors select
initAnchorLinks: ->
frame = document.getElementById('alchemy_preview_window')
elements = frame.contentDocument?.getElementsByTagName('*') || []
elements = frame.contentDocument?.querySelectorAll('[id]') || []
if elements.length > 0
for element in elements
if element.id
@$anchor_link.append("<option value='##{element.id}'>##{element.id}</option>")
@$anchor_link.append("<option value='##{element.id}'>##{element.id}</option>")
else
@$anchor_link.html("<option>#{Alchemy.t('No anchors found')}</option>")
return
Expand Down
69 changes: 67 additions & 2 deletions app/assets/stylesheets/alchemy/elements.scss
Original file line number Diff line number Diff line change
Expand Up @@ -606,17 +606,35 @@
input[type="text"] {
padding-right: $form-field-addon-width + 2 * $default-padding;
}

&.with-anchor {
input[type="text"] {
padding-right: 22px + $form-field-addon-width + $default-padding;
}
}
}

&.with-size-select {
input[type="text"] {
padding-right: $form-field-addon-width + 2 * $default-padding;
}

&.with-anchor {
input[type="text"] {
padding-right: 22px + $form-field-addon-width + $default-padding;
}
}

&.with-level-select {
input[type="text"] {
padding-right: 2 * ($form-field-addon-width + $default-padding);
}

&.with-anchor {
input[type="text"] {
padding-right: 16px + 2 * ($form-field-addon-width + $default-padding);
}
}
}
}
}
Expand Down Expand Up @@ -714,6 +732,18 @@ select.long {
input[type="text"] {
padding-right: 2 * ($form-field-addon-width + $default-padding);
}

&.with-anchor {
input[type="text"] {
padding-right: 26px + 2 * ($form-field-addon-width + $default-padding);
}
}
}

&.with-anchor {
input[type="text"] {
padding-right: 26px;
}
}

&.missing {
Expand Down Expand Up @@ -962,9 +992,44 @@ textarea.has_tinymce {
}

.ingredient-date--label,
.essence_date--label {
.essence_date--label,
.edit-ingredient-anchor-link {
position: absolute;
right: 2 * $default-padding;
bottom: $form-field-height / 2;
bottom: 15px;
margin: 0 !important;
}

.edit-ingredient-anchor-link {
right: $default-padding;

> a {
padding: $default-padding;
}

.linkable & {
right: 2 * $form-field-addon-width + $default-padding;
}

.with-size-select & {
right: $form-field-addon-width + $default-padding;
}

.with-level-select & {
right: $form-field-addon-width + $default-padding;
}

.with-level-select.with-size-select & {
right: 2 * $form-field-addon-width + $default-padding;
}
}

.ingredient-properties-link {
position: absolute;
right: 2px;
bottom: 15px;

> a {
padding: $default-padding;
}
}
2 changes: 1 addition & 1 deletion app/controllers/alchemy/admin/ingredients_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def update
private

def ingredient_params
params.require(:ingredient).permit(@ingredient.class.stored_attributes[:data])
params[:ingredient]&.permit(@ingredient.class.stored_attributes[:data]) || {}
end

def load_croppable_resource
Expand Down
22 changes: 22 additions & 0 deletions app/controllers/alchemy/api/ingredients_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

module Alchemy
class Api::IngredientsController < Api::BaseController
# Returns all ingredients as json object
#
# You can either load all or only these for :element_id or :page_id param
#
def index
@ingredients = Alchemy::Ingredient.accessible_by(current_ability, :index)

if params[:page_id].present?
@ingredients = @ingredients
.where(alchemy_page_versions: { page_id: params[:page_id] })
.merge(Alchemy::PageVersion.drafts)
.joins(element: :page_version)
end

render json: @ingredients, adapter: :json, root: "ingredients"
end
end
end
1 change: 1 addition & 0 deletions app/decorators/alchemy/ingredient_editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def css_classes
respond_to?(:level_options) && level_options.many? ? "with-level-select" : nil,
respond_to?(:size_options) && size_options.many? ? "with-size-select" : nil,
settings[:linkable] ? "linkable" : nil,
settings[:anchor] ? "with-anchor" : nil,
].compact
end

Expand Down
3 changes: 3 additions & 0 deletions app/models/alchemy/ingredients/headline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ module Ingredients
# A text headline
#
class Headline < Alchemy::Ingredient
include DomIds

store_accessor :data,
:dom_id,
:level,
:size

Expand Down
3 changes: 3 additions & 0 deletions app/models/alchemy/ingredients/text.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ module Ingredients
# Optionally it can have a link
#
class Text < Alchemy::Ingredient
include DomIds

store_accessor :data,
:dom_id,
:link,
:link_target,
:link_title,
Expand Down
32 changes: 32 additions & 0 deletions app/models/concerns/alchemy/dom_ids.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

module Alchemy
module DomIds
extend ActiveSupport::Concern

RESERVED_ANCHOR_SETTING_VALUES = %w[false from_value true]

included do
before_validation :parameterize_dom_id,
if: -> { settings[:anchor].to_s == "true" }
before_validation :set_dom_id_from_value,
if: -> { settings[:anchor].to_s == "from_value" }
before_validation :set_dom_id_to_fixed_value,
if: -> { !RESERVED_ANCHOR_SETTING_VALUES.include? settings[:anchor].to_s }
end

private

def parameterize_dom_id
self.dom_id = dom_id&.parameterize
end

def set_dom_id_from_value
self.dom_id = value&.parameterize
end

def set_dom_id_to_fixed_value
self.dom_id = settings[:anchor]&.parameterize
end
end
end
11 changes: 11 additions & 0 deletions app/serializers/alchemy/ingredient_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Alchemy
class IngredientSerializer < ActiveModel::Serializer
attributes :id,
:role,
:value,
:element_id,
:data
end
end
3 changes: 3 additions & 0 deletions app/views/alchemy/admin/elements/update.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

$errors.hide();
$el.trigger('SaveElement.Alchemy', {previewText: '<%= j sanitize(@element.preview_text) %>'});
<% @element.ingredients.select { |i| i.settings[:anchor] }.each do |ingredient| %>
Alchemy.IngredientAnchorLink.updateIcon(<%= ingredient.id %>, <%= ingredient.dom_id.present? %>);
<% end %>
Alchemy.growl('<%= Alchemy.t(:element_saved) %>');
Alchemy.PreviewWindow.refresh(function() {
Alchemy.ElementEditors.focusElementPreview(<%= @element.id %>);
Expand Down
4 changes: 4 additions & 0 deletions app/views/alchemy/admin/ingredients/_dom_id_fields.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<% disabled = ingredient.settings[:anchor].to_s != "true" %>
<%= f.input :dom_id,
disabled: disabled,
hint: disabled && Alchemy.t(:automatic_anchor_notice) %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<% if ingredient.settings[:anchor] %>
<%= render "dom_id_fields", f: f, ingredient: ingredient %>
<% end %>
3 changes: 3 additions & 0 deletions app/views/alchemy/admin/ingredients/_text_fields.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<% if ingredient.settings[:anchor] %>
<%= render "dom_id_fields", f: f, ingredient: ingredient %>
<% end %>
7 changes: 7 additions & 0 deletions app/views/alchemy/admin/ingredients/update.js.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Alchemy.closeCurrentDialog(
<% if @ingredient.settings[:anchor] %>
function() {
Alchemy.IngredientAnchorLink.updateIcon(<%= @ingredient.id %>, <%= @ingredient.dom_id.present? %>);
}
<% end %>
);
3 changes: 2 additions & 1 deletion app/views/alchemy/admin/partials/_routes.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
order_admin_elements_path: '<%= alchemy.order_admin_elements_path %>',
link_admin_pages_path: '<%= alchemy.link_admin_pages_path %>',
api_pages_path: '<%= alchemy.api_pages_path %>',
api_elements_path: '<%= alchemy.api_elements_path %>'
api_elements_path: '<%= alchemy.api_elements_path %>',
api_ingredients_path: '<%= alchemy.api_ingredients_path %>'
};
</script>
6 changes: 6 additions & 0 deletions app/views/alchemy/ingredients/_headline_editor.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<%= element_form.fields_for(:ingredients, headline_editor.ingredient) do |f| %>
<%= ingredient_label(headline_editor) %>
<%= f.text_field :value, id: nil %>

<% if headline_editor.settings[:anchor] %>
<%= render "alchemy/ingredients/shared/anchor", ingredient_editor: headline_editor %>
<% end %>

<% if has_level_select %>
<div class="input-addon right<%= " second" if has_size_select %>">
<%= f.select :level,
Expand All @@ -15,6 +20,7 @@
{ class: "custom-select", title: f.object.class.human_attribute_name(:level) } %>
</div>
<% end %>

<% if has_size_select %>
<div class="input-addon right">
<%= f.select :size, options_for_select(headline_editor.size_options, headline_editor.size),
Expand Down
1 change: 1 addition & 0 deletions app/views/alchemy/ingredients/_headline_view.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<%= content_tag "h#{headline_view.level}",
headline_view.value,
id: headline_view.dom_id.presence,
class: [
headline_view.size ? "h#{headline_view.size}" : nil,
html_options[:class]
Expand Down
3 changes: 3 additions & 0 deletions app/views/alchemy/ingredients/_text_editor.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
class: text_editor.settings[:linkable] ? "text_with_icon" : "",
id: nil,
type: text_editor.settings[:input_type] || "text" %>
<% if text_editor.settings[:anchor] %>
<%= render "alchemy/ingredients/shared/anchor", ingredient_editor: text_editor %>
<% end %>
<% if text_editor.settings[:linkable] %>
<%= f.hidden_field :link, "data-link-value": true, id: nil %>
<%= f.hidden_field :link_title, "data-link-title": true, id: nil %>
Expand Down
10 changes: 7 additions & 3 deletions app/views/alchemy/ingredients/_text_view.html.erb
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
<%- options = local_assigns.fetch(:options, {}) -%>
<%- html_options = local_assigns.fetch(:html_options, {}) -%>
<%- if text_view.link.blank? ||
text_view.settings_value(:disable_link, options) -%>
<%= text_view.value -%>
<%- if text_view.link.blank? || text_view.settings_value(:disable_link, options) -%>
<%- if text_view.dom_id.present? -%>
<%= content_tag :a, text_view.value, id: text_view.dom_id %>
<% else %>
<%= text_view.value -%>
<%- end -%>
<%- else -%>
<%= link_to(
text_view.value,
url_for(text_view.link),
{
id: text_view.dom_id.presence,
title: text_view.link_title,
target: (text_view.link_target == "blank" ? "_blank" : nil),
'data-link-target' => text_view.link_target
Expand Down
9 changes: 9 additions & 0 deletions app/views/alchemy/ingredients/shared/_anchor.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<span class="edit-ingredient-anchor-link">
<%= link_to_dialog render_icon(:bookmark, { style: ingredient_editor.dom_id.present? ? "solid" : "regular" }),
alchemy.edit_admin_ingredient_path(id: ingredient_editor.id),
{
title: Alchemy.t(:edit_anchor),
size: "380x125"
},
title: Alchemy.t(:edit_anchor) %>
</span>
7 changes: 5 additions & 2 deletions config/locales/alchemy.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,8 @@ en:

add_nested_element: "Add %{name}"
anchor: 'Anchor'
anchor_link_headline: "You can link to an element anchor from the actual page."
anchor_link_headline: You can link to an anchor from the current page.
automatic_anchor_notice: The anchor is generated automatically.
attribute_fixed: Value can't be changed for this page type
back: 'back'
locked_pages: "Active pages"
Expand Down Expand Up @@ -467,7 +468,7 @@ en:
image_name: "Name: %{name}"
image_title: "Title-tag"
internal_link_headline: "Search for a page to link to by entering its name into the Page select."
internal_link_page_elements_explanation: "Additionally you can choose an anchor to an element from selected page."
internal_link_page_elements_explanation: "Additionally you can choose an anchor to link to from selected page."
"item copied to clipboard": "Copied %{name} to clipboard"
"item moved to clipboard": "Moved %{name} to clipboard"
"item removed from clipboard": "Removed %{name} from clipboard"
Expand Down Expand Up @@ -845,6 +846,8 @@ en:
crop_from: Crop from
crop_size: Crop size
picture_id: Bild
alchemy/ingredient:
dom_id: Anchor
alchemy/language:
country_code: "Country code"
language_code: "Language code"
Expand Down
Loading