Skip to content

Commit

Permalink
Merge pull request #2395 from tvdeyen/add-anchors
Browse files Browse the repository at this point in the history
Allow to add anchors to ingredients
  • Loading branch information
tvdeyen authored Dec 16, 2022
2 parents a272e0c + bf48d42 commit 8b4da49
Show file tree
Hide file tree
Showing 39 changed files with 634 additions and 44 deletions.
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) %>
3 changes: 3 additions & 0 deletions app/views/alchemy/admin/ingredients/_headline_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 %>
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

0 comments on commit 8b4da49

Please sign in to comment.