decidim-core 0.31.0.rc1 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity_cell.rb +6 -0
  3. data/app/cells/decidim/author/avatar.erb +1 -7
  4. data/app/cells/decidim/author/badge.erb +6 -0
  5. data/app/cells/decidim/author/name.erb +1 -5
  6. data/app/cells/decidim/author/show.erb +47 -15
  7. data/app/cells/decidim/author_cell.rb +10 -0
  8. data/app/cells/decidim/profile/avatar.erb +0 -2
  9. data/app/cells/decidim/profile/badge.erb +3 -3
  10. data/app/cells/decidim/profile/details.erb +2 -1
  11. data/app/cells/decidim/user_activity_cell.rb +6 -1
  12. data/app/controllers/decidim/download_your_data_controller.rb +5 -2
  13. data/app/helpers/decidim/map_helper.rb +1 -1
  14. data/app/helpers/decidim/menu_helper.rb +8 -2
  15. data/app/mailers/decidim/user_group_mailer.rb +21 -0
  16. data/app/models/decidim/private_export.rb +4 -0
  17. data/app/models/decidim/user.rb +2 -2
  18. data/app/packs/src/decidim/controllers/dropdown/controller.js +2 -2
  19. data/app/packs/src/decidim/controllers/editor/controller.js +4 -1
  20. data/app/packs/src/decidim/controllers/form_filter/controller.js +6 -0
  21. data/app/packs/src/decidim/datepicker/generate_datepicker.js +6 -0
  22. data/app/packs/src/decidim/datepicker/generate_timepicker.js +9 -0
  23. data/app/packs/src/decidim/index.js +10 -5
  24. data/app/packs/src/decidim/map/controller/markers.js +3 -1
  25. data/app/packs/src/decidim/refactor/moved/check_boxes_tree.js +1 -0
  26. data/app/packs/stylesheets/decidim/_accordion.scss +4 -4
  27. data/app/packs/stylesheets/decidim/_author.scss +15 -1
  28. data/app/packs/stylesheets/decidim/_cards.scss +5 -1
  29. data/app/packs/stylesheets/decidim/_dropdown.scss +22 -5
  30. data/app/packs/stylesheets/decidim/_filters.scss +1 -1
  31. data/app/packs/stylesheets/decidim/_footer.scss +5 -0
  32. data/app/packs/stylesheets/decidim/_profile.scss +6 -6
  33. data/app/packs/stylesheets/decidim/editor.scss +3 -1
  34. data/app/services/decidim/download_your_data_exporter.rb +15 -4
  35. data/app/services/decidim/open_data_exporter.rb +2 -1
  36. data/app/validators/etiquette_validator.rb +2 -2
  37. data/app/validators/password_validator.rb +3 -1
  38. data/app/views/decidim/application/_document.html.erb +2 -2
  39. data/app/views/decidim/download_your_data/_export.html.erb +1 -1
  40. data/app/views/decidim/export_mailer/download_your_data_export.html.erb +1 -1
  41. data/app/views/decidim/export_mailer/export.html.erb +1 -1
  42. data/app/views/decidim/shared/_filters.html.erb +1 -1
  43. data/app/views/decidim/user_group_mailer/notify_deprecation_to_member.html.erb +4 -1
  44. data/app/views/decidim/user_group_mailer/notify_deprecation_to_owner.html.erb +1 -4
  45. data/app/views/decidim/user_group_mailer/notify_user_group_patched.html.erb +21 -0
  46. data/app/views/layouts/decidim/_application.html.erb +1 -1
  47. data/config/locales/ca-IT.yml +30 -18
  48. data/config/locales/ca.yml +28 -16
  49. data/config/locales/cs.yml +6 -7
  50. data/config/locales/de.yml +2 -11
  51. data/config/locales/en.yml +22 -10
  52. data/config/locales/es-MX.yml +20 -8
  53. data/config/locales/es-PY.yml +20 -8
  54. data/config/locales/es.yml +20 -8
  55. data/config/locales/eu.yml +60 -48
  56. data/config/locales/fi-plain.yml +20 -9
  57. data/config/locales/fi.yml +20 -9
  58. data/config/locales/fr-CA.yml +22 -10
  59. data/config/locales/fr.yml +22 -10
  60. data/config/locales/it.yml +2 -0
  61. data/config/locales/ja.yml +22 -10
  62. data/config/locales/ro-RO.yml +1 -1
  63. data/config/locales/ru.yml +0 -1
  64. data/config/locales/sl.yml +0 -1
  65. data/db/data/20251108232118_add_dummy_migration.rb +11 -0
  66. data/db/migrate/20250217192438_convert_user_groups_into_users.rb +19 -1
  67. data/db/migrate/20250819110800_convert_private_exports_id_to_uuid.rb +55 -0
  68. data/decidim-core.gemspec +1 -0
  69. data/lib/decidim/asset_router/storage.rb +8 -8
  70. data/lib/decidim/core/engine.rb +9 -0
  71. data/lib/decidim/core/test/factories.rb +7 -2
  72. data/lib/decidim/core/test/shared_examples/comments_examples.rb +24 -0
  73. data/lib/decidim/core/version.rb +1 -1
  74. data/lib/decidim/form_builder.rb +1 -1
  75. data/lib/decidim/shakapacker/configuration.rb +5 -1
  76. data/lib/tasks/upgrade/clean.rake +11 -0
  77. data/lib/tasks/upgrade/decidim_fix_action_log.rake +28 -0
  78. data/lib/tasks/upgrade/user_groups_migration.rake +33 -0
  79. metadata +25 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48ac792c6ae7948c567414610e5822df045b3f9f6c4dc638a8e5813a6043c6aa
4
- data.tar.gz: 4b19fd5477f45a51b816eeabdb81961c181ef8d4a46d1fbe920c9fbe0a5cef25
3
+ metadata.gz: 644720c93a8b08cdb4aa839557a74298774aef2719321671ca3bee677d8e93a6
4
+ data.tar.gz: e603634ef33432a213f5122b5e00c11614085d95fcc35e4a3c1b180d582427d8
5
5
  SHA512:
6
- metadata.gz: a44f376207a878d0ac6d250dc82d82e0c9c6ae06ca51f97140fe65e01eecc01780bbfe49c9434bf4dde2631141912d765e6052897ab07ae360ee57848f9e28a3
7
- data.tar.gz: ac20b49d78c501a49757a3483e483e60b47ba4de6652ffbafbe6d97d535ab8a25aa6b1d21b8a93870116eff87c8f6489e2171238265393c3976e8ce22dd704aa
6
+ metadata.gz: a98933696db00e724d2b7ae56c14e3cc718be1026234a9a36679ef3678395a3fe4308be1ff8d9fd59aa233f7c056459e2466089a2f9e21368c9df6b37c722505
7
+ data.tar.gz: 2dc98284fe86eb99c409a4f418b00ec6baba661476bfa2114b518a63dfba5313b33e268099a1e90703d98ec4ec438c8e1e4a30e51717ca63c2fb2f57b20e4664
@@ -12,6 +12,12 @@ module Decidim
12
12
  return unless renderable?
13
13
 
14
14
  render
15
+ rescue NoMethodError => e
16
+ # Soft-deleted components or participatory spaces could cause errors
17
+ # when rendering activities. We log them for further inspection but
18
+ # avoid breaking the entire activity feed.
19
+ Rails.logger.error("Error rendering activity cell for #{model.id}: #{e.message}")
20
+ nil
15
21
  end
16
22
 
17
23
  # Since activity logs could be linked to resource no longer available
@@ -1,9 +1,3 @@
1
1
  <%= content_tag :span, class: "author__avatar-container" do %>
2
- <% if profile_path? %>
3
- <%= link_to profile_path do %>
4
- <%= render :avatar_image %>
5
- <% end %>
6
- <% else %>
7
- <%= render :avatar_image %>
8
- <% end %>
2
+ <%= render :avatar_image %>
9
3
  <% end %>
@@ -0,0 +1,6 @@
1
+ <% if show_badge? %>
2
+ <span class="author__badge">
3
+ <%= icon "star-s-fill" %>
4
+ <span class="sr-only"><%= officialization_text %></span>
5
+ </span>
6
+ <% end %>
@@ -1,5 +1 @@
1
- <% if profile_path? %>
2
- <%= link_to display_name, profile_path, class: "author__name underline" %>
3
- <% else %>
4
- <%= content_tag :span, display_name, class: "author__name" %>
5
- <% end %>
1
+ <%= content_tag :span, display_name, class: "author__name" %>
@@ -1,26 +1,58 @@
1
1
  <%= content_tag(:p, class: :author, data: ) do %>
2
- <%= content_tag :span, class: "author__container#{" is-compact" if layout == :compact}" do %>
3
- <% if layout == :compact %>
4
- <%= render :avatar %>
2
+ <% if layout == :compact %>
5
3
 
6
- <span>
7
- <%= render :name %>
4
+ <% if profile_path? %>
5
+ <%= link_to profile_path, class: "author__container is-compact" do %>
6
+ <%= render :avatar %>
7
+
8
+ <span>
9
+ <span class="flex gap-2">
10
+ <%= render :name %>
11
+ <%= render :badge %>
12
+ </span>
8
13
 
9
- <% context_actions.each do |action| %>
10
- <%= render action %>
11
- <% end %>
12
- </span>
13
- <% elsif layout == :avatar %>
14
- <%= render :avatar %>
14
+ <% context_actions.each do |action| %>
15
+ <%= render action %>
16
+ <% end %>
17
+ </span>
18
+ <% end %>
15
19
  <% else %>
16
- <%= render :avatar %>
17
- <%= render :name %>
20
+ <%= content_tag :span, class: "author__container is-compact" do %>
21
+ <%= render :avatar %>
22
+
23
+ <span>
24
+ <%= render :name %>
25
+
26
+ <% context_actions.each do |action| %>
27
+ <%= render action %>
28
+ <% end %>
29
+ </span>
30
+ <% end %>
31
+ <% end %>
32
+ <% elsif layout == :avatar %>
33
+ <%= render :avatar %>
34
+ <% else %>
35
+ <% if profile_path? %>
36
+ <%= link_to profile_path, class: "author__container" do %>
37
+ <%= render :avatar %>
38
+
39
+ <%= render :name %>
40
+ <%= render :badge %>
41
+ <% end %>
42
+ <% else %>
43
+ <%= content_tag :span, class: "author__container" do %>
44
+ <%= render :avatar %>
45
+ <%= render :name %>
46
+ <%= render :badge %>
47
+ <% end %>
18
48
  <% end %>
19
49
  <% end %>
20
50
 
21
51
  <% if layout == :default %>
22
- <% context_actions.each do |action| %>
23
- <%= render action %>
52
+ <%= content_tag :span, class: "author__container" do %>
53
+ <% context_actions.each do |action| %>
54
+ <%= render action %>
55
+ <% end %>
24
56
  <% end %>
25
57
  <% end %>
26
58
  <% end %>
@@ -163,5 +163,15 @@ module Decidim
163
163
  def resource_name
164
164
  @resource_name ||= from_context.class.name.demodulize.underscore
165
165
  end
166
+
167
+ def show_badge?
168
+ return false unless model.respond_to? :officialized?
169
+
170
+ model.officialized?
171
+ end
172
+
173
+ def officialization_text
174
+ translated_attribute(model.officialized_as).presence || t("decidim.profiles.show.officialized")
175
+ end
166
176
  end
167
177
  end
@@ -2,6 +2,4 @@
2
2
  <div class="profile__avatar">
3
3
  <%= image_tag avatar_url, alt: t("decidim.author.avatar", name: decidim_sanitize(presented_profile.name)) %>
4
4
  </div>
5
-
6
- <%= render :badge if show_badge? %>
7
5
  </div>
@@ -1,4 +1,4 @@
1
- <div class="profile__avatar-badge">
1
+ <span class="profile__details-badge">
2
2
  <%= icon "star-s-fill" %>
3
- <span class="sr-only"><%= officialization_text %></span>
4
- </div>
3
+ <%= officialization_text %>
4
+ </span>
@@ -1,7 +1,8 @@
1
1
  <div class="profile__details">
2
- <h1 class="h3">
2
+ <h1 class="h3 flex gap-2">
3
3
  <%= presented_profile.name %>
4
4
  <span class="sr-only"><%= user_tabs.find { |tab_item| is_active_link?(tab_item[:path]) }&.dig(:text) %> (<%= presented_profile.name %>)</span>
5
+ <%= render :badge if show_badge? %>
5
6
  </h1>
6
7
  <div class="profile__details-data">
7
8
  <% details_items.each do |detail| %>
@@ -11,7 +11,12 @@ module Decidim
11
11
  end
12
12
 
13
13
  def activities
14
- context[:activities]
14
+ resource_ids_to_filter = context[:activities].select { |log| log[:action] == "delete" && log[:resource_type] == "Decidim::Comments::Comment" }.map(&:resource_id)
15
+ if resource_ids_to_filter.any?
16
+ context[:activities].where.not("resource_id in (?) AND resource_type = ?", resource_ids_to_filter, "Decidim::Comments::Comment")
17
+ else
18
+ context[:activities]
19
+ end
15
20
  end
16
21
 
17
22
  def resource_types
@@ -43,7 +43,10 @@ module Decidim
43
43
  def download_file
44
44
  enforce_permission_to(:download, :user, current_user:)
45
45
 
46
- if private_export.expired?
46
+ if private_export.blank?
47
+ flash[:error] = t("decidim.account.download_your_data_export.export_not_found")
48
+ redirect_to download_your_data_path
49
+ elsif private_export.expired?
47
50
  flash[:error] = t("decidim.account.download_your_data_export.export_expired")
48
51
  redirect_to download_your_data_path
49
52
  elsif private_export.file.attached?
@@ -57,7 +60,7 @@ module Decidim
57
60
  private
58
61
 
59
62
  def private_export
60
- @private_export ||= current_user.private_exports.find(params[:uuid])
63
+ @private_export ||= current_user.private_exports.where(uuid: params[:uuid]).first
61
64
  end
62
65
 
63
66
  def help_definitions
@@ -81,7 +81,7 @@ module Decidim
81
81
 
82
82
  help = content_tag(:div, class: "map__skip-container") do
83
83
  sr_content = content_tag(:p, t("screen_reader_explanation", scope: "decidim.map.dynamic"), class: "sr-only")
84
- link = link_to(t("skip_button", scope: "decidim.map.dynamic"), "##{bottom_id}", class: "map__skip")
84
+ link = link_to(t("skip_button", scope: "decidim.map.dynamic"), "##{bottom_id}", class: "map__skip", "data-skip-to-content": true)
85
85
 
86
86
  sr_content + link
87
87
  end
@@ -68,8 +68,14 @@ module Decidim
68
68
  @menu_highlighted_participatory_process ||= (
69
69
  # The queries already include the order by weight
70
70
  Decidim::ParticipatoryProcesses::OrganizationParticipatoryProcesses.new(current_organization) |
71
- Decidim::ParticipatoryProcesses::PromotedParticipatoryProcesses.new
72
- ).first
71
+ Decidim::ParticipatoryProcesses::PromotedParticipatoryProcesses.new
72
+ ).select(&:published?).map { |process| remove_private_space_if_not_private_user(process) }&.compact&.first
73
+ end
74
+
75
+ def remove_private_space_if_not_private_user(process)
76
+ return nil if process.private_space == true && !process.can_participate?(current_user)
77
+
78
+ process
73
79
  end
74
80
 
75
81
  def home_content_block_menu
@@ -2,6 +2,9 @@
2
2
 
3
3
  module Decidim
4
4
  class UserGroupMailer < ApplicationMailer
5
+ # This is a mailer that aids migration from the old user_groups to the new user group system
6
+ # This should be used only in the scope of the migration process
7
+ # @deprecated This mailer will be removed in decidim v0.32.0
5
8
  def notify_deprecation_to_owner(group)
6
9
  with_user(group) do
7
10
  @group_name = group.name
@@ -13,6 +16,9 @@ module Decidim
13
16
  end
14
17
  end
15
18
 
19
+ # This is a mailer that aids migration from the old user_groups to the new user group system
20
+ # This should be used only in the scope of the migration process
21
+ # @deprecated This mailer will be removed in decidim v0.32.0
16
22
  def notify_deprecation_to_member(user, group_name, group_email)
17
23
  with_user(user) do
18
24
  @user = user
@@ -24,5 +30,20 @@ module Decidim
24
30
  mail(to: user.email, subject:)
25
31
  end
26
32
  end
33
+
34
+ # This is a mailer that aids migration from the old user_groups to the new user group system
35
+ # This should be used only in the scope of the migration process
36
+ # @deprecated This mailer will be removed in decidim v0.32.0
37
+ def notify_user_group_patched(group, user, password)
38
+ with_user(user) do
39
+ @group = group
40
+ @user = user
41
+ @password = password
42
+ @organization = user.organization
43
+
44
+ subject = I18n.t("notify_user_group_patched.subject", scope: "decidim.user_group_mailer")
45
+ mail(to: user.email, subject:)
46
+ end
47
+ end
27
48
  end
28
49
  end
@@ -12,6 +12,10 @@ module Decidim
12
12
 
13
13
  default_scope { order(created_at: :desc) }
14
14
 
15
+ before_create do |record|
16
+ record.uuid = SecureRandom.uuid
17
+ end
18
+
15
19
  def expired?
16
20
  expires_at < Time.zone.now
17
21
  end
@@ -66,8 +66,8 @@ module Decidim
66
66
  scope :active_after_notification, lambda {
67
67
  where("current_sign_in_at > (extended_data->'inactivity_notification'->>'sent_at')::timestamp")
68
68
  }
69
- scope :user_group, -> { where("#{arel_table.name}.extended_data @> ?", Arel.sql({ group: true }.to_json)) }
70
- scope :not_user_group, -> { where.not("#{arel_table.name}.extended_data @> ?", Arel.sql({ group: true }.to_json)) }
69
+ scope :user_group, -> { where("#{arel_table.name}.extended_data @> ?", { group: true }.to_json) }
70
+ scope :not_user_group, -> { where.not("#{arel_table.name}.extended_data @> ?", { group: true }.to_json) }
71
71
 
72
72
  attr_accessor :newsletter_notifications
73
73
 
@@ -116,10 +116,10 @@ export default class extends Controller {
116
116
  changeStyleOfSelectedElement() {
117
117
  this.element.addEventListener("click", function(event) {
118
118
  event.target.parentNode.parentNode.querySelectorAll("a").forEach((link) => {
119
- link.parentNode.classList.remove("dropdown__item-hovered")
119
+ link.parentNode.classList.remove("dropdown__item-opened");
120
120
  })
121
121
 
122
- event.target.parentNode.classList.add("dropdown__item-hovered")
122
+ event.target.parentNode.classList.add("dropdown__item-opened")
123
123
  })
124
124
  }
125
125
  }
@@ -3,6 +3,9 @@ import createEditor from "src/decidim/editor";
3
3
 
4
4
  export default class extends Controller {
5
5
  connect() {
6
- this.editor = createEditor(this.element)
6
+ if (!this.element.dataset.editorInitialized) {
7
+ this.editor = createEditor(this.element);
8
+ this.element.dataset.editorInitialized = true;
9
+ }
7
10
  }
8
11
  }
@@ -213,6 +213,12 @@ export default class extends Controller {
213
213
  this.changeEvents = false;
214
214
  this._clearForm();
215
215
 
216
+ // Prevent filtering again on anchor link "Skip to main content", "Skip map", or "Skip to results"
217
+ const filterSkipValues = [...document.querySelectorAll("[data-skip-to-content]")].map((el) => el.hash);
218
+ if (filterSkipValues.includes(window.location.hash)) {
219
+ return;
220
+ }
221
+
216
222
  const filterParams = this._parseLocationFilterValues();
217
223
  const currentOrder = this._parseLocationOrderValue();
218
224
 
@@ -14,12 +14,18 @@ export default function generateDatePicker(input, row, formats) {
14
14
  date.setAttribute("id", `${input.id}_date`);
15
15
  date.setAttribute("type", "text");
16
16
  date.setAttribute("aria-label", input.dataset.dateLabel);
17
+ if (input.attributes.disabled) {
18
+ date.setAttribute("disabled", input.attributes.disabled);
19
+ };
17
20
 
18
21
  const calendar = document.createElement("button");
19
22
  calendar.innerHTML = icon("calendar-line");
20
23
  calendar.setAttribute("class", "datepicker__calendar-button");
21
24
  calendar.setAttribute("type", "button");
22
25
  calendar.setAttribute("aria-label", input.dataset.buttonDateLabel);
26
+ if (input.attributes.disabled) {
27
+ calendar.setAttribute("disabled", input.attributes.disabled);
28
+ };
23
29
 
24
30
  dateColumn.appendChild(date);
25
31
  dateColumn.appendChild(calendar);
@@ -1,4 +1,6 @@
1
1
  /* eslint-disable require-jsdoc */
2
+ /* eslint max-lines: ["error", 310] */
3
+
2
4
  import icon from "src/decidim/refactor/moved/icon"
3
5
  import { changeHourDisplay, changeMinuteDisplay, formatDate, hourDisplay, minuteDisplay, formatTime, setHour, setMinute, updateTimeValue, updateInputValue } from "src/decidim/datepicker/datepicker_functions"
4
6
  import { timeKeyDownListener, timeBeforeInputListener } from "src/decidim/datepicker/datepicker_listeners";
@@ -14,12 +16,19 @@ export default function generateTimePicker(input, row, formats) {
14
16
  time.setAttribute("id", `${input.id}_time`);
15
17
  time.setAttribute("type", "text");
16
18
  time.setAttribute("aria-label", input.dataset.timeLabel);
19
+ if (input.attributes.disabled) {
20
+ time.setAttribute("disabled", input.attributes.disabled);
21
+ };
17
22
 
18
23
  const clock = document.createElement("button");
19
24
  clock.innerHTML = icon("time-line")
20
25
  clock.setAttribute("class", "datepicker__clock-button");
21
26
  clock.setAttribute("type", "button");
22
27
  clock.setAttribute("aria-label", input.dataset.buttonTimeLabel);
28
+ if (input.attributes.disabled) {
29
+ clock.setAttribute("disabled", input.attributes.disabled);
30
+ };
31
+
23
32
 
24
33
  timeColumn.appendChild(time);
25
34
  timeColumn.appendChild(clock);
@@ -70,16 +70,21 @@ const deprecate = (element, targetController, oldSyntax) => {
70
70
  return;
71
71
  }
72
72
 
73
- console.warn(element)
74
73
  console.warn(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - data-controller="${targetController}" - ${window.location.href}`)
75
- // eslint-disable-next-line no-alert
76
- alert(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - data-controller="${targetController}"`)
74
+
75
+ if (typeof window.Decidim.dev !== "undefined" && window.Decidim.dev === true) {
76
+ // eslint-disable-next-line no-alert
77
+ alert(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - data-controller="${targetController}"`)
78
+ }
77
79
  }
78
80
 
79
81
  const deprecationMessage = (element, oldSyntax, newSyntax) => {
80
82
  console.warn(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - ${newSyntax}`)
81
- // eslint-disable-next-line no-alert
82
- alert(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - ${newSyntax}`)
83
+
84
+ if (typeof window.Decidim.dev !== "undefined" && window.Decidim.dev === true) {
85
+ // eslint-disable-next-line no-alert
86
+ alert(`[Decidim] ${oldSyntax} is deprecated. Please use the new version of this component - ${newSyntax}`)
87
+ }
83
88
  }
84
89
 
85
90
  window.deprecate = deprecate;
@@ -67,7 +67,9 @@ export default class MapMarkersController extends MapController {
67
67
  }
68
68
 
69
69
  clearMarkers() {
70
- this.map.removeLayer(this.markerClusters);
70
+ if (this.markerClusters !== null) {
71
+ this.map.removeLayer(this.markerClusters);
72
+ }
71
73
  this.markerClusters = new L.MarkerClusterGroup();
72
74
  this.map.addLayer(this.markerClusters);
73
75
  }
@@ -116,6 +116,7 @@ export default class CheckBoxesTree {
116
116
  const indeterminateSiblings = totalCheckSiblings.filter((checkbox) => checkbox.indeterminate)
117
117
 
118
118
  if (checkedSiblings.length === 0 && indeterminateSiblings.length === 0) {
119
+ parentCheck.checked = false;
119
120
  parentCheck.indeterminate = false;
120
121
  } else if (checkedSiblings.length === totalCheckSiblings.length && indeterminateSiblings.length === 0) {
121
122
  parentCheck.checked = true;
@@ -1,13 +1,13 @@
1
- [data-controller="accordion"] [id*="panel"][aria-hidden="true"] {
1
+ [data-controller*="accordion"] [id*="panel"][aria-hidden="true"] {
2
2
  display: none;
3
3
  }
4
4
 
5
- [data-controller="accordion"]
5
+ [data-controller*="accordion"]
6
6
  [id*="comment"][class="comment-reply"][aria-hidden="true"] {
7
7
  display: none;
8
8
  }
9
9
 
10
- [data-controller="accordion"]
10
+ [data-controller*="accordion"]
11
11
  [id*="comment"][class="comment-reply"][aria-hidden="false"] {
12
12
  display: block;
13
13
  }
@@ -57,7 +57,7 @@
57
57
  @apply mr-4;
58
58
  }
59
59
 
60
- &-section.content-block__description[data-controller="accordion"] {
60
+ &-section.content-block__description[data-controller*="accordion"] {
61
61
  @apply pb-4 mb-4 text-md;
62
62
 
63
63
  padding-left: 1.85rem;
@@ -1,5 +1,11 @@
1
1
  .author {
2
- @apply flex items-center [&>*:not(:first-child)]:pl-5 [&>*:not(:last-child)]:pr-6 divide-gray-3 divide-x text-sm text-gray-2;
2
+ @apply flex items-center divide-gray-3 divide-x text-sm text-gray-2;
3
+
4
+ a.author__container {
5
+ .author__name {
6
+ @apply underline text-secondary font-bold;
7
+ }
8
+ }
3
9
 
4
10
  &__container {
5
11
  @apply flex items-center gap-2.5 first:[&>*]:flex-none;
@@ -14,6 +20,10 @@
14
20
 
15
21
  &-container {
16
22
  @apply rounded-full overflow-hidden inline-block w-6 h-6 align-top;
23
+
24
+ &:focus-within {
25
+ @apply ring-2 ring-primary;
26
+ }
17
27
  }
18
28
 
19
29
  &-counter {
@@ -29,6 +39,10 @@
29
39
  @apply text-secondary font-semibold;
30
40
  }
31
41
 
42
+ &__badge svg {
43
+ @apply grid overflow-hidden place-items-center bg-primary rounded-full w-4 h-4 text-white fill-current;
44
+ }
45
+
32
46
  &__metadata {
33
47
  @apply flex items-center gap-1 text-gray-2 text-sm;
34
48
 
@@ -59,7 +59,11 @@
59
59
  }
60
60
 
61
61
  &-title {
62
- @apply h4 text-secondary;
62
+ @apply h4;
63
+ }
64
+
65
+ a &-title {
66
+ @apply text-secondary;
63
67
 
64
68
  overflow: hidden;
65
69
  display: -webkit-box;
@@ -106,12 +106,9 @@
106
106
  @apply text-gray-2 mr-1;
107
107
  }
108
108
  }
109
- }
110
109
 
111
- &:hover,
112
- &-hovered {
113
- > .dropdown__button {
114
- @apply text-white no-underline;
110
+ &:hover {
111
+ @apply text-white no-underline !bg-secondary;
115
112
 
116
113
  span {
117
114
  @apply text-white;
@@ -122,6 +119,26 @@
122
119
  }
123
120
  }
124
121
  }
122
+
123
+ &-opened {
124
+ > .dropdown__button {
125
+ @apply text-secondary no-underline;
126
+
127
+ span {
128
+ @apply text-secondary;
129
+ }
130
+
131
+ svg {
132
+ fill: rgb(var(--secondary-rgb) / var(--tw-bg-opacity)) !important;
133
+ }
134
+
135
+ &:hover {
136
+ svg {
137
+ fill: white !important;
138
+ }
139
+ }
140
+ }
141
+ }
125
142
  }
126
143
 
127
144
  &__bottom {
@@ -129,7 +129,7 @@
129
129
 
130
130
  /* overwrite default dropdowns */
131
131
  [data-target="dropdown-menu-filters"] {
132
- @apply px-0 justify-start [&>span]:text-gray-2;
132
+ @apply px-0 justify-start [&>span]:text-gray-2 md:hidden;
133
133
 
134
134
  > svg {
135
135
  @apply h-4 w-4 text-gray;
@@ -4,6 +4,11 @@ footer {
4
4
 
5
5
  &__top {
6
6
  @apply hidden lg:flex flex-row gap-8 container py-10;
7
+
8
+ .prose a,
9
+ .prose strong {
10
+ @apply text-white;
11
+ }
7
12
  }
8
13
 
9
14
  &__down {
@@ -9,18 +9,18 @@
9
9
  &-container {
10
10
  @apply w-24 h-24 relative;
11
11
  }
12
+ }
13
+
14
+ &__details {
15
+ @apply pb-3 space-y-2;
12
16
 
13
17
  &-badge {
14
- @apply absolute top-full right-0 -translate-y-full grid place-items-center w-6 h-6 rounded-full overflow-hidden bg-primary border border-white;
18
+ @apply flex items-center gap-1 text-sm text-gray-2;
15
19
 
16
20
  svg {
17
- @apply w-4 h-4 text-white fill-current;
21
+ @apply w-4 h-4 inline-block bg-primary rounded-full text-white fill-current;
18
22
  }
19
23
  }
20
- }
21
-
22
- &__details {
23
- @apply pb-3 space-y-2;
24
24
 
25
25
  &-data {
26
26
  @apply flex flex-wrap gap-x-6 gap-y-4;
@@ -84,7 +84,9 @@
84
84
  }
85
85
 
86
86
  .ProseMirror {
87
- @apply relative p-2.5 outline-0 min-h-full prose max-w-none prose-headings:first:mt-0 prose-p:first:mt-0 prose-ul:first:mt-0 prose-ol:first:mt-0 prose-blockquote:first:mt-0 prose-pre:first:mt-0;
87
+ @apply relative p-2.5 outline-0 resize-y overflow-hidden prose max-w-none prose-headings:first:mt-0 prose-p:first:mt-0 prose-ul:first:mt-0 prose-ol:first:mt-0 prose-blockquote:first:mt-0 prose-pre:first:mt-0;
88
+
89
+ min-height: inherit;
88
90
 
89
91
  &.ProseMirror-focused,
90
92
  &.dialog-open {