decidim-core 0.29.1 → 0.29.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/cells/decidim/activity_cell.rb +0 -3
- data/app/cells/decidim/author/show.erb +5 -4
- data/app/cells/decidim/author_cell.rb +26 -0
- data/app/cells/decidim/card_s/show.erb +5 -3
- data/app/cells/decidim/diff_cell.rb +4 -0
- data/app/cells/decidim/newsletter_templates/image_text_cta_cell.rb +1 -1
- data/app/cells/decidim/translation_bar/show.erb +2 -2
- data/app/cells/decidim/translation_bar_cell.rb +1 -1
- data/app/commands/decidim/destroy_account.rb +3 -0
- data/app/controllers/decidim/doorkeeper/credentials_controller.rb +1 -1
- data/app/controllers/decidim/links_controller.rb +1 -1
- data/app/controllers/decidim/profiles_controller.rb +4 -0
- data/app/helpers/concerns/decidim/user_role_checker.rb +46 -0
- data/app/helpers/decidim/cta_button_helper.rb +1 -1
- data/app/helpers/decidim/map_helper.rb +6 -1
- data/app/helpers/decidim/sanitize_helper.rb +11 -2
- data/app/models/decidim/attachment.rb +1 -1
- data/app/packs/src/decidim/append_redirect_url_to_modals.js +14 -6
- data/app/packs/src/decidim/direct_uploads/upload_field.js +21 -8
- data/app/packs/src/decidim/index.js +3 -0
- data/app/packs/src/decidim/remote_tooltips.js +38 -0
- data/app/packs/src/decidim/toggle.js +1 -1
- data/app/packs/src/decidim/tooltips.js +42 -22
- data/app/packs/stylesheets/decidim/_labels.scss +1 -1
- data/app/packs/stylesheets/decidim/_profile.scss +1 -1
- data/app/packs/stylesheets/decidim/_progress-bar.scss +1 -1
- data/app/packs/stylesheets/decidim/legacy/conference-diploma.scss +2 -1
- data/app/presenters/decidim/attachment_presenter.rb +1 -1
- data/app/services/decidim/base_diff_renderer.rb +26 -2
- data/app/services/decidim/email_notification_generator.rb +14 -5
- data/app/views/decidim/pages/_tabbed.html.erb +2 -2
- data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile_tablet.html.erb +1 -1
- data/config/locales/ar.yml +16 -0
- data/config/locales/bn-BD.yml +1 -0
- data/config/locales/bs-BA.yml +98 -0
- data/config/locales/ca.yml +13 -9
- data/config/locales/cs.yml +5 -0
- data/config/locales/de.yml +18 -14
- data/config/locales/el.yml +7 -0
- data/config/locales/en.yml +4 -0
- data/config/locales/es-MX.yml +5 -1
- data/config/locales/es-PY.yml +5 -1
- data/config/locales/es.yml +11 -7
- data/config/locales/eu.yml +198 -181
- data/config/locales/fi-plain.yml +4 -0
- data/config/locales/fi.yml +39 -35
- data/config/locales/fr-CA.yml +6 -2
- data/config/locales/fr.yml +5 -1
- data/config/locales/ga-IE.yml +9 -0
- data/config/locales/gl.yml +8 -0
- data/config/locales/hu.yml +3 -3
- data/config/locales/id-ID.yml +8 -0
- data/config/locales/is-IS.yml +8 -1
- data/config/locales/it.yml +19 -0
- data/config/locales/ja.yml +15 -13
- data/config/locales/lb.yml +9 -0
- data/config/locales/lt.yml +5 -1
- data/config/locales/lv.yml +8 -0
- data/config/locales/nl.yml +10 -1
- data/config/locales/no.yml +9 -0
- data/config/locales/pl.yml +1 -1
- data/config/locales/pt-BR.yml +2 -1
- data/config/locales/pt.yml +14 -0
- data/config/locales/ro-RO.yml +258 -135
- data/config/locales/ru.yml +8 -0
- data/config/locales/sk.yml +9 -1
- data/config/locales/sv.yml +7 -7
- data/config/locales/tr-TR.yml +10 -1
- data/config/locales/uk.yml +8 -1
- data/config/locales/zh-CN.yml +9 -0
- data/config/locales/zh-TW.yml +8 -0
- data/config/routes.rb +1 -0
- data/decidim-core.gemspec +4 -1
- data/lib/decidim/api/functions/component_list.rb +1 -1
- data/lib/decidim/api/functions/participatory_space_finder_base.rb +11 -1
- data/lib/decidim/api/interfaces/participatory_space_interface.rb +1 -1
- data/lib/decidim/api/types/component_type.rb +7 -0
- data/lib/decidim/api/types/user_group_type.rb +4 -0
- data/lib/decidim/api/types/user_type.rb +4 -0
- data/lib/decidim/attributes/rich_text.rb +38 -0
- data/lib/decidim/attributes/time_with_zone.rb +11 -1
- data/lib/decidim/attributes.rb +2 -0
- data/lib/decidim/content_parsers/blob_parser.rb +93 -0
- data/lib/decidim/content_parsers.rb +1 -0
- data/lib/decidim/content_renderers/blob_renderer.rb +90 -0
- data/lib/decidim/content_renderers.rb +1 -0
- data/lib/decidim/core/engine.rb +29 -1
- data/lib/decidim/core/test/factories.rb +28 -0
- data/lib/decidim/core/test/shared_examples/authorable_interface_examples.rb +1 -1
- data/lib/decidim/core/test/shared_examples/comments_examples.rb +15 -2
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/diffy_extension.rb +18 -0
- data/lib/decidim/form_builder.rb +1 -1
- data/lib/decidim/map/autocomplete.rb +1 -0
- data/lib/decidim/participatory_space_user.rb +4 -0
- data/lib/decidim/query_extensions.rb +0 -26
- data/lib/decidim/settings_manifest.rb +2 -0
- data/lib/decidim/translatable_attributes.rb +6 -1
- data/lib/tasks/upgrade/decidim_fix_categorization.rake +34 -8
- metadata +28 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83ba22655575833dce2ce20e91060aa195abccf10cd5d185dbadbc04e79fcd93
|
4
|
+
data.tar.gz: a53c89d6b0c01046408b2872119406ffbd1a80a50b943256b872c8575b069e4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07db0787cc696b27044eb8765477b338a16f7263cd29d9231dfd8e7145f5c4b63b19753a90b4d35c5e7614c1a69d47104435fcb2ecf12badea9a1163709ccbef
|
7
|
+
data.tar.gz: 034e07f956b0ad613172afdd3a56cbb3e37e273555537919073bf75b63dfe8b2ab7ec5502ecf87fb224cd5d525ad009a01e0db6742815ce61eec643c0fa1f1ae
|
@@ -105,9 +105,6 @@ module Decidim
|
|
105
105
|
hash << I18n.locale.to_s
|
106
106
|
hash << model.class.name.underscore
|
107
107
|
hash << model.cache_key_with_version if model.respond_to?(:cache_key_with_version)
|
108
|
-
if (author_cell = author)
|
109
|
-
hash.push(Digest::MD5.hexdigest(author_cell.send(:cache_hash)))
|
110
|
-
end
|
111
108
|
|
112
109
|
hash.join(Decidim.cache_key_separator)
|
113
110
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
<%# If the options hash has the demo key it means we are in the decidim-design engine, so it is not a real-world scenario with actual users %>
|
2
|
+
<% tooltip = options.has_key?(:demo) ? { tooltip: render(:profile_minicard).html_safe } : nil %>
|
3
|
+
<%= content_tag(:span, class: :author, data: ) do %>
|
4
|
+
<%= content_tag :span, class: "author__container#{" is-compact" if layout == :compact}", data: tooltip do %>
|
4
5
|
<% if layout == :compact %>
|
5
6
|
<%= render :avatar %>
|
6
7
|
|
@@ -24,4 +25,4 @@
|
|
24
25
|
<%= render action %>
|
25
26
|
<% end %>
|
26
27
|
<% end %>
|
27
|
-
|
28
|
+
<% end %>
|
@@ -47,8 +47,26 @@ module Decidim
|
|
47
47
|
@context_actions_options ||= options[:context_actions].map(&:to_sym)
|
48
48
|
end
|
49
49
|
|
50
|
+
def profile_minicard
|
51
|
+
render
|
52
|
+
end
|
53
|
+
|
50
54
|
private
|
51
55
|
|
56
|
+
# If the options hash has the demo key it means we are in the decidim-design engine,
|
57
|
+
# so it is not a real-world scenario with actual users
|
58
|
+
def data
|
59
|
+
@data ||= begin
|
60
|
+
internal_data = { author: true }
|
61
|
+
if has_tooltip? && !options.has_key?(:demo)
|
62
|
+
internal_data["remote_tooltip"] = true
|
63
|
+
internal_data["tooltip-url"] = decidim.profile_tooltip_path(raw_model.nickname)
|
64
|
+
end
|
65
|
+
|
66
|
+
internal_data
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
52
70
|
def layout
|
53
71
|
@layout ||= LAYOUTS.include?(options[:layout]) ? options[:layout] : :default
|
54
72
|
end
|
@@ -160,5 +178,13 @@ module Decidim
|
|
160
178
|
def resource_name
|
161
179
|
@resource_name ||= from_context.class.name.demodulize.underscore
|
162
180
|
end
|
181
|
+
|
182
|
+
def has_tooltip?
|
183
|
+
return false if model.deleted?
|
184
|
+
return false if model.respond_to?(:blocked?) && model.blocked?
|
185
|
+
return true if options.has_key?(:tooltip)
|
186
|
+
|
187
|
+
model.has_tooltip?
|
188
|
+
end
|
163
189
|
end
|
164
190
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
-
<%=
|
1
|
+
<%= content_tag :div, id: dom_id(resource), class: "card__search" do %>
|
2
2
|
<%= content_tag title_tag, class: "h4 card__search-title" do %>
|
3
|
-
<%=
|
3
|
+
<%= link_to resource_path, class: "card__search" do %>
|
4
|
+
<%= title %>
|
5
|
+
<% end %>
|
4
6
|
<% end %>
|
5
7
|
<% if metadata_cell.present? %>
|
6
8
|
<div class="card__search-metadata">
|
7
|
-
|
9
|
+
<%= cell metadata_cell, resource, links: false %>
|
8
10
|
</div>
|
9
11
|
<% end %>
|
10
12
|
<% end %>
|
@@ -70,6 +70,10 @@ module Decidim
|
|
70
70
|
|
71
71
|
# DiffRenderer class for the current_version's item; falls back to `BaseDiffRenderer`.
|
72
72
|
def diff_renderer_class
|
73
|
+
renderer_class = "#{current_version.item_type}DiffRenderer".safe_constantize
|
74
|
+
|
75
|
+
return renderer_class if renderer_class
|
76
|
+
|
73
77
|
if current_version.item_type.deconstantize == "Decidim"
|
74
78
|
"#{current_version.item_type.pluralize}::DiffRenderer".constantize
|
75
79
|
else
|
@@ -50,7 +50,7 @@ module Decidim
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def main_image_url
|
53
|
-
newsletter.template.images_container.attached_uploader(:main_image).url
|
53
|
+
newsletter.template.images_container.attached_uploader(:main_image).url
|
54
54
|
end
|
55
55
|
|
56
56
|
def organization_primary_color
|
@@ -37,6 +37,9 @@ module Decidim
|
|
37
37
|
current_user.name = ""
|
38
38
|
current_user.nickname = ""
|
39
39
|
current_user.email = ""
|
40
|
+
current_user.personal_url = ""
|
41
|
+
current_user.about = ""
|
42
|
+
current_user.notifications_sending_frequency = "none"
|
40
43
|
current_user.delete_reason = @form.delete_reason
|
41
44
|
current_user.admin = false if current_user.admin?
|
42
45
|
current_user.deleted_at = Time.current
|
@@ -28,7 +28,7 @@ module Decidim
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def avatar_url
|
31
|
-
avatar_url = current_resource_owner.attached_uploader(:avatar).url
|
31
|
+
avatar_url = current_resource_owner.attached_uploader(:avatar).url
|
32
32
|
return unless avatar_url
|
33
33
|
|
34
34
|
unless %r{^https?://}.match? avatar_url
|
@@ -41,7 +41,7 @@ module Decidim
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def escape_url(external_url)
|
44
|
-
before_fragment, fragment = external_url.split("#", 2)
|
44
|
+
before_fragment, fragment = URI.decode_www_form_component(external_url).split("#", 2)
|
45
45
|
escaped_before_fragment = URI::Parser.new.escape(before_fragment)
|
46
46
|
|
47
47
|
if fragment
|
@@ -27,6 +27,10 @@ module Decidim
|
|
27
27
|
redirect_to profile_activity_path(nickname: params[:nickname])
|
28
28
|
end
|
29
29
|
|
30
|
+
def tooltip
|
31
|
+
render json: { data: cell("decidim/author", profile_holder.presenter).profile_minicard }
|
32
|
+
end
|
33
|
+
|
30
34
|
def following
|
31
35
|
@content_cell = "decidim/following"
|
32
36
|
@title_key = "following"
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module UserRoleChecker
|
5
|
+
# Shared behaviour for signed_in admins
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def user_has_any_role?(user, participatory_space = nil, broad_check: false)
|
11
|
+
return false unless user
|
12
|
+
|
13
|
+
[
|
14
|
+
user.admin,
|
15
|
+
user.roles.any?,
|
16
|
+
participatory_process_user_role?(user, participatory_space, broad_check:),
|
17
|
+
assembly_user_role?(user, participatory_space, broad_check:),
|
18
|
+
conference_user_role?(user, participatory_space, broad_check:)
|
19
|
+
].any?
|
20
|
+
end
|
21
|
+
|
22
|
+
def participatory_process_user_role?(user, participatory_process = nil, broad_check: false)
|
23
|
+
return false unless Decidim.module_installed?(:participatory_processes)
|
24
|
+
return Decidim::ParticipatoryProcessUserRole.exists?(user:) if broad_check
|
25
|
+
return false unless participatory_process.is_a?(Decidim::ParticipatoryProcess)
|
26
|
+
|
27
|
+
Decidim::ParticipatoryProcessUserRole.exists?(user:, participatory_process:)
|
28
|
+
end
|
29
|
+
|
30
|
+
def assembly_user_role?(user, assembly = nil, broad_check: false)
|
31
|
+
return false unless Decidim.module_installed?(:assemblies)
|
32
|
+
return Decidim::AssemblyUserRole.exists?(user:) if broad_check
|
33
|
+
return false unless assembly.is_a?(Decidim::Assembly)
|
34
|
+
|
35
|
+
Decidim::AssemblyUserRole.exists?(user:, assembly:)
|
36
|
+
end
|
37
|
+
|
38
|
+
def conference_user_role?(user, conference = nil, broad_check: false)
|
39
|
+
return false unless Decidim.module_installed?(:conferences)
|
40
|
+
return Decidim::ConferenceUserRole.exists?(user:) if broad_check
|
41
|
+
return false unless conference.is_a?(Decidim::Conference)
|
42
|
+
|
43
|
+
Decidim::ConferenceUserRole.exists?(user:, conference:)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -16,7 +16,7 @@ module Decidim
|
|
16
16
|
# Finds the CTA button path to reuse it in other places.
|
17
17
|
def cta_button_path
|
18
18
|
if current_organization.cta_button_path.present?
|
19
|
-
current_organization.cta_button_path
|
19
|
+
"/#{current_organization.cta_button_path}"
|
20
20
|
elsif Decidim::ParticipatoryProcess.where(organization: current_organization).published.any?
|
21
21
|
decidim_participatory_processes.participatory_processes_path
|
22
22
|
elsif current_user
|
@@ -35,7 +35,12 @@ module Decidim
|
|
35
35
|
data: { "external-link": "text-only" }
|
36
36
|
}.merge(map_html_options)
|
37
37
|
return link_to(map_url, html_options) do
|
38
|
-
|
38
|
+
# We also add the latitude and the longitude to prevent the Workbox cache to be overly aggressive when updating a map
|
39
|
+
image_tag decidim.static_map_path(
|
40
|
+
sgid: resource.to_sgid.to_s,
|
41
|
+
latitude: resource.latitude,
|
42
|
+
longitude: resource.longitude
|
43
|
+
), alt: "#{map_service_brand} - #{address_text}"
|
39
44
|
end
|
40
45
|
end
|
41
46
|
end
|
@@ -37,13 +37,22 @@ module Decidim
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
+
# Converts the blob and blob variant references to blob URLs.
|
41
|
+
def decidim_rich_text(html, **)
|
42
|
+
renderer = Decidim::ContentProcessor.renderer_klass(:blob).constantize.new(html)
|
43
|
+
renderer.render(**)
|
44
|
+
end
|
45
|
+
|
40
46
|
def decidim_sanitize_editor(html, options = {})
|
41
47
|
content_tag(:div, decidim_sanitize(html, options), class: %w(rich-text-display))
|
42
48
|
end
|
43
49
|
|
44
50
|
def decidim_sanitize_editor_admin(html, options = {})
|
45
51
|
html = Decidim::IframeDisabler.new(html, options).perform
|
46
|
-
decidim_sanitize_editor(
|
52
|
+
decidim_sanitize_editor(
|
53
|
+
decidim_rich_text(html),
|
54
|
+
{ scrubber: Decidim::AdminInputScrubber.new }.merge(options)
|
55
|
+
)
|
47
56
|
end
|
48
57
|
|
49
58
|
def decidim_html_escape(text)
|
@@ -51,7 +60,7 @@ module Decidim
|
|
51
60
|
end
|
52
61
|
|
53
62
|
def decidim_url_escape(text)
|
54
|
-
decidim_html_escape(text).sub(
|
63
|
+
decidim_html_escape(text).sub(/^\s*javascript:/, "")
|
55
64
|
end
|
56
65
|
|
57
66
|
def decidim_sanitize_translated(text)
|
@@ -53,10 +53,18 @@ $(() => {
|
|
53
53
|
}
|
54
54
|
|
55
55
|
$(document).on("click.zf.trigger", (event) => {
|
56
|
-
|
57
|
-
const
|
56
|
+
// Try to get the <a> directly or find the closest parent <a>
|
57
|
+
const $target = $(event.target).closest("a");
|
58
58
|
|
59
|
-
if
|
59
|
+
// Check if an <a> was found
|
60
|
+
if (!$target) {
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
|
64
|
+
const dialogTarget = `#${$target.data("dialog-open")}`;
|
65
|
+
const redirectUrl = $target.data("redirectUrl");
|
66
|
+
|
67
|
+
if (!dialogTarget || !redirectUrl) {
|
60
68
|
return;
|
61
69
|
}
|
62
70
|
|
@@ -64,10 +72,10 @@ $(() => {
|
|
64
72
|
attr("id", "redirect_url").
|
65
73
|
attr("name", "redirect_url").
|
66
74
|
attr("value", redirectUrl).
|
67
|
-
appendTo(`${
|
75
|
+
appendTo(`${dialogTarget} form`);
|
68
76
|
|
69
|
-
$(`${
|
70
|
-
const querystring = jQuery.param({
|
77
|
+
$(`${dialogTarget} a`).attr("href", (index, href) => {
|
78
|
+
const querystring = jQuery.param({"redirect_url": redirectUrl});
|
71
79
|
return href + (href.match(/\?/) ? "&" : "?") + querystring;
|
72
80
|
});
|
73
81
|
});
|
@@ -21,6 +21,7 @@ const updateActiveUploads = (modal) => {
|
|
21
21
|
const files = document.querySelector(`[data-active-uploads=${modal.modal.id}]`)
|
22
22
|
const previousId = Array.from(files.querySelectorAll("[type=hidden][id]"))
|
23
23
|
const isMultiple = modal.options.multiple
|
24
|
+
const isTitled = modal.options.titled
|
24
25
|
|
25
26
|
// fastest way to clean children nodes
|
26
27
|
files.textContent = ""
|
@@ -34,16 +35,26 @@ const updateActiveUploads = (modal) => {
|
|
34
35
|
let hidden = ""
|
35
36
|
if (file.hiddenField) {
|
36
37
|
// if there is hiddenField, this file is new
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
let fileField = null;
|
39
|
+
if (isMultiple) {
|
40
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][file]`
|
41
|
+
} else if (isTitled) {
|
42
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][file]`
|
43
|
+
} else {
|
44
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}]`;
|
45
|
+
}
|
40
46
|
|
41
47
|
hidden = `<input type="hidden" name="${fileField}" value="${file.hiddenField}" />`
|
42
48
|
} else {
|
43
49
|
// otherwise, we keep the attachmentId
|
44
|
-
|
45
|
-
|
46
|
-
|
50
|
+
let fileField = null;
|
51
|
+
if (isMultiple) {
|
52
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][id]`;
|
53
|
+
} else if (isTitled) {
|
54
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}][id]`;
|
55
|
+
} else {
|
56
|
+
fileField = `${modal.options.resourceName}[${modal.options.addAttribute}]`;
|
57
|
+
}
|
47
58
|
|
48
59
|
// convert all node attributes to string
|
49
60
|
const attributes = Array.from(previousId.find(({ id }) => id === file.attachmentId).attributes).reduce((acc, { name, value }) => `${acc} ${name}="${value}"`, "")
|
@@ -51,10 +62,12 @@ const updateActiveUploads = (modal) => {
|
|
51
62
|
hidden += `<input type="hidden" name="${fileField}" value="${file.attachmentId}" />`
|
52
63
|
}
|
53
64
|
|
54
|
-
if (
|
65
|
+
if (isTitled) {
|
55
66
|
const titleValue = modal.modal.querySelectorAll('input[type="text"]')[ix].value
|
56
67
|
// NOTE - Renaming the attachment is not supported when multiple uploader is disabled
|
57
|
-
const titleField =
|
68
|
+
const titleField = isMultiple
|
69
|
+
? `${modal.options.resourceName}[${modal.options.addAttribute}][${ix}][title]`
|
70
|
+
: `${modal.options.resourceName}[${modal.options.addAttribute}][title]`
|
58
71
|
hidden += `<input type="hidden" name="${titleField}" value="${escapeQuotes(titleValue)}" />`
|
59
72
|
|
60
73
|
title = titleValue
|
@@ -69,6 +69,7 @@ import handleNotificationActions from "src/decidim/notifications_actions"
|
|
69
69
|
import RemoteModal from "src/decidim/remote_modal"
|
70
70
|
import selectActiveIdentity from "src/decidim/identity_selector_dialog"
|
71
71
|
import createTooltip from "src/decidim/tooltips"
|
72
|
+
import fetchRemoteTooltip from "src/decidim/remote_tooltips"
|
72
73
|
import createToggle from "src/decidim/toggle"
|
73
74
|
import {
|
74
75
|
createAccordion,
|
@@ -195,6 +196,8 @@ const initializer = (element = document) => {
|
|
195
196
|
// Initialize data-toggles
|
196
197
|
element.querySelectorAll("[data-toggle]").forEach((elem) => createToggle(elem))
|
197
198
|
|
199
|
+
element.querySelectorAll("[data-remote-tooltip]").forEach((elem) => fetchRemoteTooltip(elem))
|
200
|
+
|
198
201
|
element.querySelectorAll(".new_report").forEach((elem) => changeReportFormBehavior(elem))
|
199
202
|
}
|
200
203
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import createTooltip from "src/decidim/tooltips"
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Given the following HTML structure,
|
5
|
+
* <span data-remote-tooltip="true" tooltip-url="some url" data-author="true">
|
6
|
+
* <span></span>
|
7
|
+
* </span>
|
8
|
+
*
|
9
|
+
* This function will check if the HTMLElement where is attached to has a child, and will add a data tooltip attribute
|
10
|
+
* to the respective child in order to attach the fetched HTML content fetched under a json key as the content of the
|
11
|
+
* HTML tooltip. The DOM structure is expected to be like follows:
|
12
|
+
*
|
13
|
+
* <span data-remote-tooltip="true" tooltip-url="some url" data-author="true">
|
14
|
+
* <span data-tooltip="HTML content from json data field"></span>
|
15
|
+
* </span>
|
16
|
+
*
|
17
|
+
* @param {HTMLElement} node The element holding the initialization data
|
18
|
+
* @returns {void}
|
19
|
+
*/
|
20
|
+
export default async function(node) {
|
21
|
+
const container = node.firstElementChild;
|
22
|
+
|
23
|
+
if (container) {
|
24
|
+
const response = await fetch(node.dataset.tooltipUrl, {
|
25
|
+
headers: {
|
26
|
+
"Content-Type": "application/json"
|
27
|
+
}
|
28
|
+
});
|
29
|
+
if (response.ok) {
|
30
|
+
const json = await response.json();
|
31
|
+
|
32
|
+
container.dataset.tooltip = json.data;
|
33
|
+
createTooltip(container);
|
34
|
+
} else {
|
35
|
+
console.error(response.status, response.statusText);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
@@ -9,7 +9,7 @@ export default function createToggle(component) {
|
|
9
9
|
const { toggle } = component.dataset
|
10
10
|
|
11
11
|
if (!component.id) {
|
12
|
-
// when component has no id, we enforce to have
|
12
|
+
// when component has no id, we enforce it to have one
|
13
13
|
component.id = `toggle-${Math.random().toString(36).substring(7)}`
|
14
14
|
}
|
15
15
|
|
@@ -63,9 +63,32 @@ export default function(node) {
|
|
63
63
|
// append to dom hidden, to apply css transitions
|
64
64
|
tooltip.setAttribute("aria-hidden", true)
|
65
65
|
|
66
|
-
|
67
|
-
|
66
|
+
// used to detect if the user is on a mobile device by checking the user agent
|
67
|
+
const useMobile = (/Mobi|Android/i).test(navigator.userAgent);
|
68
|
+
|
69
|
+
// used not to collapse tooltip
|
70
|
+
let removeTooltip = () => {
|
71
|
+
tooltip.setAttribute("aria-hidden", "true");
|
72
|
+
}
|
73
|
+
|
74
|
+
// used to allow clicks outside the tooltip to take place on the page or device
|
75
|
+
const OutsideClick = (event) => {
|
76
|
+
if (!tooltip.contains(event.target) && event.target !== node) {
|
77
|
+
removeTooltip();
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
// function called again to allow clicks outside the tooltip to collapse the tooltip
|
82
|
+
removeTooltip = () => {
|
83
|
+
tooltip.setAttribute("aria-hidden", "true");
|
84
|
+
document.removeEventListener("click", OutsideClick)
|
85
|
+
}
|
86
|
+
|
87
|
+
const toggleTooltip = (event) => {
|
88
|
+
event.preventDefault();
|
89
|
+
// if the tooltip is visible in the DOM, hide it otherwise display
|
68
90
|
if (tooltip.getAttribute("aria-hidden") === "false") {
|
91
|
+
tooltip.setAttribute("aria-hidden", "true");
|
69
92
|
return
|
70
93
|
}
|
71
94
|
|
@@ -106,27 +129,24 @@ export default function(node) {
|
|
106
129
|
tooltip.style.left = `${positionX}px`
|
107
130
|
|
108
131
|
tooltip.setAttribute("aria-hidden", false)
|
109
|
-
}
|
110
|
-
|
111
|
-
// in order to revoke the remove event when the mouse is over the trigger/tooltip
|
112
|
-
let cancelRemove = false
|
113
132
|
|
114
|
-
|
115
|
-
|
116
|
-
// give some sleep time before hiding the element from the DOM
|
117
|
-
setTimeout(() => !cancelRemove && tooltip.setAttribute("aria-hidden", true), 500);
|
133
|
+
// sleep time before hiding the element from the DOM
|
134
|
+
setTimeout(() => document.addEventListener("click", OutsideClick))
|
118
135
|
}
|
119
136
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
137
|
+
if (useMobile) {
|
138
|
+
// mobile use to click and toggle the tooltip
|
139
|
+
node.addEventListener("click", toggleTooltip);
|
140
|
+
window.addEventListener("keydown", (event) => event.key === "Escape" && removeTooltip())
|
141
|
+
} else {
|
142
|
+
// desktop use for hover and blur over tooltip
|
143
|
+
node.addEventListener("mouseenter", toggleTooltip)
|
144
|
+
node.addEventListener("mouseleave", removeTooltip)
|
145
|
+
node.addEventListener("focus", toggleTooltip)
|
146
|
+
node.addEventListener("blur", removeTooltip)
|
147
|
+
|
148
|
+
// tooltip hover listeners to prevent hiding when hovered
|
149
|
+
tooltip.addEventListener("mouseenter", () => tooltip.setAttribute("aria-hidden", false))
|
150
|
+
tooltip.addEventListener("mouseleave", removeTooltip)
|
151
|
+
}
|
132
152
|
}
|
@@ -6,7 +6,7 @@
|
|
6
6
|
--warning: #ad4910;
|
7
7
|
--bg-warning: #ffeebd;
|
8
8
|
|
9
|
-
@apply bg-background text-gray-2 rounded inline-flex items-center gap-1 px-2 font-semibold text-sm;
|
9
|
+
@apply bg-background text-gray-2 rounded inline-flex items-center gap-1 px-2 py-1 h-min font-semibold text-sm;
|
10
10
|
|
11
11
|
&.success {
|
12
12
|
@apply bg-[var(--bg-success)] text-[var(--success)];
|
@@ -37,7 +37,6 @@
|
|
37
37
|
}
|
38
38
|
|
39
39
|
.conference-diploma .diploma__logo {
|
40
|
-
border: 1px solid #333;
|
41
40
|
padding: 2rem;
|
42
41
|
}
|
43
42
|
|
@@ -47,10 +46,12 @@
|
|
47
46
|
max-height: 100%;
|
48
47
|
max-width: 100%;
|
49
48
|
margin: 0;
|
49
|
+
padding-top: 10%;
|
50
50
|
}
|
51
51
|
|
52
52
|
.conference-diploma .diploma__border {
|
53
53
|
margin: 0;
|
54
|
+
margin-top: 15%;
|
54
55
|
-moz-border-image: url("../images/decidim/pattern.png") 20 repeat;
|
55
56
|
-webkit-border-image: url("../images/decidim/pattern.png") 20 repeat;
|
56
57
|
-o-border-image: url("../images/decidim/pattern.png") 20 repeat;
|