decidim-core 0.32.0.rc3 → 0.32.1

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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/author_cell.rb +0 -4
  3. data/app/cells/decidim/comments_button_cell.rb +9 -1
  4. data/app/cells/decidim/content_blocks/highlighted_content_banner_cell.rb +1 -1
  5. data/app/cells/decidim/content_blocks/highlighted_participatory_spaces_cell.rb +1 -1
  6. data/app/cells/decidim/data_consent/category.erb +20 -33
  7. data/app/cells/decidim/footer_topics/show.erb +5 -8
  8. data/app/cells/decidim/share_widget/qr_code_modal.erb +1 -1
  9. data/app/commands/decidim/destroy_account.rb +12 -1
  10. data/app/commands/decidim/multiple_attachments_methods.rb +28 -27
  11. data/app/helpers/decidim/menu_helper.rb +0 -2
  12. data/app/jobs/decidim/process_inactive_participant_job.rb +0 -7
  13. data/app/mailers/decidim/delete_user_mailer.rb +14 -0
  14. data/app/mailers/decidim/participants_account_mailer.rb +0 -16
  15. data/app/models/decidim/user.rb +2 -2
  16. data/app/packs/src/decidim/controllers/accordion/accordion.test.js +196 -42
  17. data/app/packs/src/decidim/controllers/accordion/controller.js +93 -17
  18. data/app/packs/src/decidim/controllers/dropdown/controller.js +6 -0
  19. data/app/packs/src/decidim/controllers/dropdown/dropdown.test.js +28 -0
  20. data/app/packs/src/decidim/controllers/main_menu/controller.js +37 -0
  21. data/app/packs/src/decidim/controllers/main_menu/main_menu.test.js +100 -0
  22. data/app/packs/src/decidim/controllers/mention/controller.js +296 -140
  23. data/app/packs/src/decidim/controllers/mention/input_mentions.test.js +120 -457
  24. data/app/packs/src/decidim/controllers/multiple_mentions/controller.js +68 -32
  25. data/app/packs/src/decidim/controllers/multiple_mentions/input_multiple_mentions.test.js +30 -23
  26. data/app/packs/src/decidim/controllers/password_toggler/controller.js +22 -1
  27. data/app/packs/src/decidim/controllers/password_toggler/password_toggler.test.js +11 -20
  28. data/app/packs/src/decidim/direct_uploads/upload_modal.js +2 -1
  29. data/app/packs/src/decidim/editor/common/suggestion.js +3 -1
  30. data/app/packs/src/decidim/editor/extensions/indent/index.js +9 -0
  31. data/app/packs/src/decidim/geocoding/reverse_geocoding.js +15 -5
  32. data/app/packs/src/decidim/geocoding/reverse_geocoding.test.js +197 -0
  33. data/app/packs/src/decidim/index.js +2 -2
  34. data/app/packs/src/decidim/map/controller.js +10 -1
  35. data/app/packs/src/decidim/refactor/moved/focus_guard.js +12 -12
  36. data/app/packs/src/decidim/refactor/moved/focus_guard.test.js +136 -0
  37. data/app/packs/stylesheets/decidim/_cards.scss +1 -1
  38. data/app/packs/stylesheets/decidim/_conversations.scss +14 -0
  39. data/app/packs/stylesheets/decidim/_cookies.scss +13 -5
  40. data/app/packs/stylesheets/decidim/_documents.scss +1 -1
  41. data/app/packs/stylesheets/decidim/_dropdown.scss +1 -1
  42. data/app/packs/stylesheets/decidim/_editor_suggestions.scss +49 -0
  43. data/app/packs/stylesheets/decidim/_footer.scss +33 -1
  44. data/app/packs/stylesheets/decidim/_header.scss +12 -8
  45. data/app/packs/stylesheets/decidim/_participatory_spaces.scss +1 -1
  46. data/app/packs/stylesheets/decidim/_tom_select.scss +23 -0
  47. data/app/packs/stylesheets/decidim/application.scss +2 -0
  48. data/app/packs/stylesheets/decidim/editor.scss +2 -33
  49. data/app/packs/stylesheets/decidim/geocoding_addons.scss +10 -2
  50. data/app/presenters/decidim/footer_menu_presenter.rb +2 -4
  51. data/app/resolvers/decidim/meta_image_url_resolver.rb +7 -0
  52. data/app/uploaders/decidim/image_uploader.rb +1 -1
  53. data/app/views/decidim/delete_user_mailer/delete.html.erb +6 -0
  54. data/app/views/decidim/messaging/conversations/_error_modal.html.erb +11 -19
  55. data/app/views/decidim/messaging/conversations/error.js.erb +12 -7
  56. data/app/views/decidim/searches/_filters.html.erb +1 -1
  57. data/app/views/layouts/decidim/footer/_main.html.erb +1 -1
  58. data/app/views/layouts/decidim/footer/_main_links.html.erb +56 -28
  59. data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile.html.erb +2 -1
  60. data/config/locales/ar.yml +0 -2
  61. data/config/locales/bg.yml +0 -2
  62. data/config/locales/ca-IT.yml +27 -10
  63. data/config/locales/ca.yml +27 -10
  64. data/config/locales/cs.yml +26 -9
  65. data/config/locales/de.yml +4 -13
  66. data/config/locales/el.yml +0 -1
  67. data/config/locales/en.yml +26 -9
  68. data/config/locales/es-MX.yml +26 -9
  69. data/config/locales/es-PY.yml +26 -9
  70. data/config/locales/es.yml +26 -9
  71. data/config/locales/eu.yml +41 -10
  72. data/config/locales/fi-plain.yml +25 -8
  73. data/config/locales/fi.yml +25 -8
  74. data/config/locales/fr-CA.yml +30 -9
  75. data/config/locales/fr.yml +30 -9
  76. data/config/locales/hu.yml +0 -2
  77. data/config/locales/it.yml +0 -2
  78. data/config/locales/ja.yml +84 -19
  79. data/config/locales/lb.yml +0 -2
  80. data/config/locales/lt.yml +0 -2
  81. data/config/locales/nl.yml +0 -2
  82. data/config/locales/no.yml +0 -2
  83. data/config/locales/pl.yml +4 -4
  84. data/config/locales/pt-BR.yml +2 -11
  85. data/config/locales/pt.yml +0 -2
  86. data/config/locales/ro-RO.yml +1 -10
  87. data/config/locales/sk.yml +1 -10
  88. data/config/locales/sv.yml +0 -9
  89. data/config/locales/tr-TR.yml +0 -2
  90. data/config/locales/zh-CN.yml +0 -2
  91. data/config/locales/zh-TW.yml +0 -2
  92. data/db/migrate/20250217192438_convert_user_groups_into_users.rb +3 -2
  93. data/decidim-core.gemspec +4 -1
  94. data/lib/decidim/attachment_attributes.rb +58 -9
  95. data/lib/decidim/command.rb +1 -1
  96. data/lib/decidim/core/content_blocks/registry_manager.rb +4 -4
  97. data/lib/decidim/core/engine.rb +8 -0
  98. data/lib/decidim/core/test/factories.rb +3 -0
  99. data/lib/decidim/core/test/shared_examples/access_mode_restricted_participatory_spaces.rb +8 -5
  100. data/lib/decidim/core/test/shared_examples/access_mode_transparent_participatory_spaces.rb +14 -11
  101. data/lib/decidim/core/test/shared_examples/admin_resource_gallery_examples.rb +10 -10
  102. data/lib/decidim/core/test/shared_examples/comments_examples.rb +32 -6
  103. data/lib/decidim/core/version.rb +1 -1
  104. data/lib/decidim/core.rb +1 -1
  105. data/lib/decidim/map/autocomplete.rb +4 -3
  106. data/lib/decidim/map/dynamic_map.rb +3 -1
  107. data/lib/decidim/searchable.rb +5 -0
  108. data/lib/decidim/view_model.rb +1 -1
  109. data/lib/tasks/decidim_mailers_tasks.rake +31 -9
  110. metadata +31 -9
  111. data/app/commands/decidim/gallery_methods.rb +0 -107
  112. data/app/packs/src/decidim/vendor/tribute.js +0 -1890
  113. data/app/packs/stylesheets/decidim/_tribute.scss +0 -36
  114. data/app/views/decidim/participants_account_mailer/removal_notification.html.erb +0 -11
@@ -29,7 +29,16 @@ export default class MapController {
29
29
  }
30
30
 
31
31
  load() {
32
- this.map = L.map(this.mapId);
32
+ const zoomControl = L.control.zoom({
33
+ zoomInTitle: this.config.zoomInText || "Zoom in",
34
+ zoomOutTitle: this.config.zoomOutText || "Zoom out"
35
+ });
36
+
37
+ this.map = L.map(this.mapId, {
38
+ zoomControl: false
39
+ });
40
+
41
+ this.map.addControl(zoomControl);
33
42
 
34
43
  this.map.scrollWheelZoom.disable();
35
44
 
@@ -5,14 +5,12 @@ const focusableDisableableNodes = ["BUTTON", "INPUT", "TEXTAREA", "SELECT"];
5
5
  export default class FocusGuard {
6
6
  constructor(container) {
7
7
  this.container = container;
8
- this.guardedElement = null;
9
- this.triggerElement = null;
8
+ this.stack = [];
10
9
  }
11
10
 
12
11
  trap(element, trigger) {
13
12
  this.enable();
14
- this.guardedElement = element;
15
- this.triggerElement = trigger;
13
+ this.stack.push({ element, trigger });
16
14
  }
17
15
 
18
16
  enable() {
@@ -45,14 +43,15 @@ export default class FocusGuard {
45
43
  }
46
44
 
47
45
  disable() {
48
- const guards = this.container.querySelectorAll(`:scope > .${focusGuardClass}`);
49
- guards.forEach((guard) => guard.remove());
46
+ const current = this.stack.pop();
50
47
 
51
- this.guardedElement = null;
48
+ if (this.stack.length === 0) {
49
+ const guards = this.container.querySelectorAll(`:scope > .${focusGuardClass}`);
50
+ guards.forEach((guard) => guard.remove());
51
+ }
52
52
 
53
- if (this.triggerElement) {
54
- this.triggerElement.focus();
55
- this.triggerElement = null;
53
+ if (current && current.trigger) {
54
+ current.trigger.focus();
56
55
  }
57
56
  }
58
57
 
@@ -67,12 +66,13 @@ export default class FocusGuard {
67
66
  };
68
67
 
69
68
  handleContainerFocus(guard) {
70
- if (!this.guardedElement) {
69
+ const current = this.stack[this.stack.length - 1];
70
+ if (!current || !current.element) {
71
71
  guard.blur();
72
72
  return;
73
73
  }
74
74
 
75
- const visibleNodes = Array.from(this.guardedElement.querySelectorAll("*")).filter((item) => {
75
+ const visibleNodes = Array.from(current.element.querySelectorAll("*")).filter((item) => {
76
76
  return this.isVisible(item);
77
77
  });
78
78
 
@@ -0,0 +1,136 @@
1
+ /* global jest */
2
+
3
+ import FocusGuard from "src/decidim/refactor/moved/focus_guard"
4
+
5
+ describe("FocusGuard", () => {
6
+ let focusGuard = null;
7
+
8
+ beforeEach(() => {
9
+ document.body.innerHTML = "";
10
+ focusGuard = new FocusGuard(document.body);
11
+ });
12
+
13
+ afterEach(() => {
14
+ document.body.innerHTML = "";
15
+ });
16
+
17
+ describe("single dialog", () => {
18
+ it("creates focus guards on trap", () => {
19
+ const dialog = document.createElement("div");
20
+ const trigger = document.createElement("button");
21
+ document.body.appendChild(dialog);
22
+ document.body.appendChild(trigger);
23
+
24
+ focusGuard.trap(dialog, trigger);
25
+ const guards = document.body.querySelectorAll(".focusguard");
26
+ expect(guards.length).toBe(2);
27
+ });
28
+
29
+ it("removes focus guards and returns focus on disable", () => {
30
+ const dialog = document.createElement("div");
31
+ const trigger = document.createElement("button");
32
+ document.body.appendChild(dialog);
33
+ document.body.appendChild(trigger);
34
+
35
+ focusGuard.trap(dialog, trigger);
36
+ focusGuard.disable();
37
+
38
+ const guards = document.body.querySelectorAll(".focusguard");
39
+ expect(guards.length).toBe(0);
40
+ expect(document.activeElement).toBe(trigger);
41
+ });
42
+ });
43
+
44
+ describe("nested dialogs", () => {
45
+ it("keeps guards when inner dialog is disabled", () => {
46
+ const outerDialog = document.createElement("div");
47
+ const outerTrigger = document.createElement("button");
48
+ const innerDialog = document.createElement("div");
49
+ const innerTrigger = document.createElement("button");
50
+
51
+ document.body.appendChild(outerDialog);
52
+ document.body.appendChild(outerTrigger);
53
+ document.body.appendChild(innerDialog);
54
+ document.body.appendChild(innerTrigger);
55
+
56
+ focusGuard.trap(outerDialog, outerTrigger);
57
+ focusGuard.trap(innerDialog, innerTrigger);
58
+
59
+ expect(document.body.querySelectorAll(".focusguard").length).toBe(2);
60
+
61
+ focusGuard.disable();
62
+
63
+ expect(document.body.querySelectorAll(".focusguard").length).toBe(2);
64
+ expect(document.activeElement).toBe(innerTrigger);
65
+ });
66
+
67
+ it("removes guards when outer dialog is disabled last", () => {
68
+ const outerDialog = document.createElement("div");
69
+ const outerTrigger = document.createElement("button");
70
+ const innerDialog = document.createElement("div");
71
+ const innerTrigger = document.createElement("button");
72
+
73
+ document.body.appendChild(outerDialog);
74
+ document.body.appendChild(outerTrigger);
75
+ document.body.appendChild(innerDialog);
76
+ document.body.appendChild(innerTrigger);
77
+
78
+ focusGuard.trap(outerDialog, outerTrigger);
79
+ focusGuard.trap(innerDialog, innerTrigger);
80
+
81
+ focusGuard.disable();
82
+ focusGuard.disable();
83
+
84
+ expect(document.body.querySelectorAll(".focusguard").length).toBe(0);
85
+ expect(document.activeElement).toBe(outerTrigger);
86
+ });
87
+
88
+ it("cycles focus to the current top dialog when a guard receives focus", () => {
89
+ jest.spyOn(focusGuard, "isVisible").mockReturnValue(true);
90
+
91
+ const outerDialog = document.createElement("div");
92
+ const outerInput = document.createElement("input");
93
+ outerDialog.appendChild(outerInput);
94
+
95
+ const innerDialog = document.createElement("div");
96
+ const innerInput = document.createElement("input");
97
+ innerDialog.appendChild(innerInput);
98
+
99
+ document.body.appendChild(outerDialog);
100
+ document.body.appendChild(innerDialog);
101
+
102
+ focusGuard.trap(outerDialog, null);
103
+ focusGuard.trap(innerDialog, null);
104
+
105
+ const endGuard = document.body.querySelector('.focusguard[data-position="end"]');
106
+ endGuard.focus();
107
+
108
+ expect(document.activeElement).toBe(innerInput);
109
+ });
110
+
111
+ it("cycles focus backward to the current top dialog when the start guard receives focus", () => {
112
+ jest.spyOn(focusGuard, "isVisible").mockReturnValue(true);
113
+
114
+ const outerDialog = document.createElement("div");
115
+ const outerInput = document.createElement("input");
116
+ outerDialog.appendChild(outerInput);
117
+
118
+ const innerDialog = document.createElement("div");
119
+ const innerInput = document.createElement("input");
120
+ const innerButton = document.createElement("button");
121
+ innerDialog.appendChild(innerInput);
122
+ innerDialog.appendChild(innerButton);
123
+
124
+ document.body.appendChild(outerDialog);
125
+ document.body.appendChild(innerDialog);
126
+
127
+ focusGuard.trap(outerDialog, null);
128
+ focusGuard.trap(innerDialog, null);
129
+
130
+ const startGuard = document.body.querySelector('.focusguard[data-position="start"]');
131
+ startGuard.focus();
132
+
133
+ expect(document.activeElement).toBe(innerButton);
134
+ });
135
+ });
136
+ });
@@ -55,7 +55,7 @@
55
55
  }
56
56
 
57
57
  &-content {
58
- @apply flex flex-col gap-2.5;
58
+ @apply flex flex-col gap-2.5 min-w-0 break-words;
59
59
  }
60
60
 
61
61
  &-title {
@@ -88,6 +88,20 @@
88
88
  }
89
89
 
90
90
  &__modal {
91
+ &-error {
92
+ [data-dialog-closable] {
93
+ @apply top-6 right-6;
94
+ }
95
+
96
+ [data-dialog-container] {
97
+ @apply inline grid-cols-none;
98
+ }
99
+
100
+ [data-dialog-title] {
101
+ @apply col-span-2 md:col-span-1 md:col-start-2 grid grid-cols-[auto_1fr] items-start md:items-center gap-2 pb-4 text-left text-xl text-gray-2 md:border-b md:border-gray-3;
102
+ }
103
+ }
104
+
91
105
  &-results {
92
106
  @apply mb-20 flex flex-wrap gap-x-8 gap-y-4;
93
107
  }
@@ -65,17 +65,25 @@
65
65
  &-panel {
66
66
  @apply divide-y divide-gray-3 [&>*]:py-4 [&>p]:text-gray-2;
67
67
 
68
- &__tr {
69
- @apply flex items-center justify-between rounded first-of-type:bg-background-4;
68
+ table {
69
+ @apply w-full border-collapse;
70
70
  }
71
71
 
72
- &__th {
73
- @apply w-1/4 p-2 bg-background-4 text-gray-2 font-bold text-sm;
72
+ thead tr {
73
+ @apply bg-background-4;
74
74
  }
75
75
 
76
- &__td {
76
+ th {
77
+ @apply w-1/4 p-2 text-gray-2 font-bold text-sm text-left;
78
+ }
79
+
80
+ td {
77
81
  @apply w-1/4 p-2 text-gray-2 text-xs;
78
82
  }
83
+
84
+ tbody tr {
85
+ @apply border-b border-gray-3;
86
+ }
79
87
  }
80
88
 
81
89
  &-toggle {
@@ -3,7 +3,7 @@
3
3
  @apply divide-y divide-background [&>*]:py-4 first:[&>*]:pt-0 last:[&>*]:pb-0;
4
4
 
5
5
  > * {
6
- @apply flex items-center justify-between gap-4;
6
+ @apply grid sm:flex items-center justify-between gap-4;
7
7
  }
8
8
  }
9
9
 
@@ -1,5 +1,5 @@
1
1
  [id*="dropdown-menu"] {
2
- @apply flex flex-col py-0 mx-3.5 md:mx-0 border-t-0 border-gray-3 cursor-pointer;
2
+ @apply flex flex-col py-0 mx-3.5 md:mx-0 border-t-0 border-gray-3;
3
3
 
4
4
  &[aria-hidden="true"] {
5
5
  @apply hidden md:flex;
@@ -0,0 +1,49 @@
1
+ .editor-suggestions-props {
2
+ --editor-suggestions-border-color: #000;
3
+ --editor-suggestions-background-color: #eee;
4
+ --editor-suggestions-selected-highlight-color: #ccc;
5
+ }
6
+
7
+ .editor-suggestions {
8
+ @apply editor-suggestions-props border-0 bg-[var(--editor-suggestions-background-color)] max-w-sm drop-shadow-md rounded-md;
9
+
10
+ &:hover {
11
+ .editor-suggestions-item[data-selected]:not(:hover) {
12
+ @apply bg-transparent;
13
+ }
14
+ }
15
+
16
+ .editor-suggestions-item {
17
+ @apply flex items-center gap-2 w-full text-left py-[0.375rem] px-1.5 border-0 rounded-none text-sm;
18
+
19
+ &:first-child {
20
+ @apply rounded-t-md;
21
+ }
22
+
23
+ &:last-child {
24
+ @apply rounded-b-md;
25
+ }
26
+
27
+ &:hover,
28
+ &[data-selected] {
29
+ @apply bg-[var(--editor-suggestions-selected-highlight-color)];
30
+ }
31
+
32
+ .editor-suggestions-item-avatar {
33
+ @apply rounded-full w-6 h-6 object-cover flex-none;
34
+ }
35
+
36
+ .editor-suggestions-item-label {
37
+ @apply truncate;
38
+ }
39
+ }
40
+
41
+ .editor-suggestions-item-disabled {
42
+ @apply cursor-default;
43
+
44
+ &:hover,
45
+ &[data-selected] {
46
+ @apply bg-transparent;
47
+ }
48
+ }
49
+ }
@@ -3,12 +3,44 @@ footer {
3
3
  @apply bg-gray-4;
4
4
 
5
5
  &__top {
6
- @apply hidden lg:flex flex-row gap-8 container py-10;
6
+ @apply flex flex-col lg:flex-row gap-0 lg:gap-8 container py-10;
7
7
 
8
8
  .prose a,
9
9
  .prose strong {
10
10
  @apply text-white;
11
11
  }
12
+
13
+ .footer__nav {
14
+ @apply border-b border-white/20 lg:border-0;
15
+
16
+ ul {
17
+ @apply space-y-4 break-inside-avoid mb-4;
18
+
19
+ li a {
20
+ @apply font-semibold underline decoration-1 underline-offset-1;
21
+ }
22
+ }
23
+
24
+ > [data-controls] {
25
+ @apply flex items-center justify-between w-full py-4 lg:pointer-events-none lg:cursor-default text-left text-white font-semibold;
26
+
27
+ svg {
28
+ @apply w-4 h-4 fill-current transition-transform lg:hidden;
29
+ }
30
+
31
+ &[aria-expanded="true"] svg {
32
+ @apply rotate-180;
33
+ }
34
+ }
35
+
36
+ [id*="panel"][aria-hidden="true"] {
37
+ @apply hidden lg:block;
38
+ }
39
+
40
+ [id*="panel"][aria-hidden="false"] {
41
+ @apply block;
42
+ }
43
+ }
12
44
  }
13
45
 
14
46
  &__down {
@@ -248,6 +248,10 @@ header {
248
248
  .account-container {
249
249
  @apply before:block before:absolute before:top-16 before:right-14 before:content-[""] before:bg-white before:w-2 before:h-12 before:-mt-8;
250
250
  }
251
+
252
+ #dropdown-trigger-links-mobile-close {
253
+ @apply relative z-10;
254
+ }
251
255
  }
252
256
 
253
257
  &__item {
@@ -337,12 +341,8 @@ header {
337
341
  @apply min-w-0;
338
342
  }
339
343
 
340
- .no-interactive {
341
- @apply font-normal px-0 min-w-0;
342
- }
343
-
344
344
  &__dropdown-trigger {
345
- @apply flex flex-none rounded py-1 z-20 font-semibold;
345
+ @apply flex flex-none rounded px-2 py-1 z-20 font-semibold focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-inset;
346
346
 
347
347
  &--truncatable {
348
348
  @apply min-w-0 overflow-hidden;
@@ -357,6 +357,10 @@ header {
357
357
  }
358
358
  }
359
359
 
360
+ .no-interactive {
361
+ @apply font-normal px-0 min-w-0;
362
+ }
363
+
360
364
  &__dropdown-wrapper {
361
365
  @apply flex flex-none items-center cursor-pointer underline;
362
366
  }
@@ -449,15 +453,15 @@ header {
449
453
  }
450
454
 
451
455
  &__label-wrapper {
452
- @apply block min-w-0 flex-1 overflow-hidden;
456
+ @apply block min-w-0 flex-1;
453
457
  }
454
458
 
455
459
  &__label {
456
- @apply flex w-full items-center gap-x-2 min-w-0 overflow-hidden;
460
+ @apply flex w-11/12 items-center gap-x-2 min-w-0;
457
461
  }
458
462
 
459
463
  .menu-bar__breadcrumb-item {
460
- @apply block flex-1 min-w-0 overflow-hidden whitespace-nowrap text-ellipsis font-semibold underline;
464
+ @apply flex flex-1 min-w-0 items-center gap-x-1 whitespace-nowrap font-semibold underline focus:outline focus:outline-2 focus:outline-offset-2 rounded-sm;
461
465
  }
462
466
 
463
467
  /* overwrite default dropdown styles */
@@ -49,7 +49,7 @@
49
49
  }
50
50
 
51
51
  &__collection-content {
52
- @apply w-auto ml-6 my-4 pl-4 lg:pl-8 border-l border-[#D3D5D9];
52
+ @apply w-auto sm:ml-6 sm:my-4 sm:pl-4 lg:pl-8 sm:border-l sm:border-[#D3D5D9];
53
53
 
54
54
  p {
55
55
  @apply pt-4;
@@ -0,0 +1,23 @@
1
+ @use "tom-select/dist/scss/tom-select";
2
+
3
+ .ts {
4
+ &-control {
5
+ @apply border-gray text-md min-h-[40px];
6
+
7
+ input {
8
+ @apply font-normal text-black text-md;
9
+ }
10
+ }
11
+
12
+ &-dropdown {
13
+ @apply text-md text-gray-2 font-normal;
14
+
15
+ .option img {
16
+ @apply inline;
17
+ }
18
+
19
+ .active {
20
+ @apply bg-gray-3;
21
+ }
22
+ }
23
+ }
@@ -6,6 +6,8 @@
6
6
  // Datepicker
7
7
  @use "stylesheets/decidim/vendor/datepicker_light";
8
8
  @use "stylesheets/decidim/datepicker";
9
+ // TomSelect
10
+ @use "stylesheets/decidim/tom_select";
9
11
  // On the other hand, the following styles match with specific routes
10
12
  @use "stylesheets/decidim/header";
11
13
  @use "stylesheets/decidim/footer";
@@ -1,3 +1,5 @@
1
+ @use "stylesheets/decidim/editor_suggestions";
2
+
1
3
  .editor-props {
2
4
  --editor-active-color: #487bda;
3
5
  --editor-disabled-color: #eee;
@@ -5,12 +7,6 @@
5
7
  --editor-bubble-shadow-color: #333;
6
8
  }
7
9
 
8
- .editor-suggestions-props {
9
- --editor-suggestions-border-color: #000;
10
- --editor-suggestions-background-color: #eee;
11
- --editor-suggestions-selected-highlight-color: #ccc;
12
- }
13
-
14
10
  .editor-active {
15
11
  @apply outline outline-4 outline-[var(--editor-active-color)];
16
12
  }
@@ -186,30 +182,3 @@
186
182
  }
187
183
  }
188
184
  }
189
-
190
- .editor-suggestions {
191
- @apply editor-suggestions-props border-0 bg-[var(--editor-suggestions-background-color)] max-w-sm drop-shadow-md rounded-md;
192
-
193
- &:hover {
194
- .editor-suggestions-item[data-selected]:not(:hover) {
195
- @apply bg-transparent;
196
- }
197
- }
198
-
199
- .editor-suggestions-item {
200
- @apply block w-full text-left py-[0.375rem] px-1.5 border-0 rounded-none truncate text-sm;
201
-
202
- &:first-child {
203
- @apply rounded-t-md;
204
- }
205
-
206
- &:last-child {
207
- @apply rounded-b-md;
208
- }
209
-
210
- &:hover,
211
- &[data-selected] {
212
- @apply bg-[var(--editor-suggestions-selected-highlight-color)];
213
- }
214
- }
215
- }
@@ -1,5 +1,13 @@
1
- .geocoding-container {
2
- .user-device-location button {
1
+ .geocoding {
2
+ &__locate .geocoding__button {
3
3
  @apply mt-4;
4
+
5
+ &--locating {
6
+ @apply opacity-70 cursor-wait;
7
+ }
8
+
9
+ .geocoding__spinner {
10
+ @apply inline-block w-4 h-4 rounded-full animate-spin border-2 border-l-transparent border-y-transparent border-r-secondary align-middle;
11
+ }
4
12
  }
5
13
  }
@@ -3,10 +3,8 @@
3
3
  module Decidim
4
4
  # A presenter to render footer menu
5
5
  class FooterMenuPresenter < MenuPresenter
6
- def render
7
- content_tag(:nav, :role => "navigation", "aria-label" => @options[:label]) do
8
- safe_join([content_tag(:h2, @options[:label], class: "h5 mb-4"), render_menu])
9
- end
6
+ def render_menu_items
7
+ render_menu
10
8
  end
11
9
  end
12
10
  end
@@ -79,6 +79,9 @@ module Decidim
79
79
  image_url = image_element["src"]
80
80
  next if image_url.blank?
81
81
 
82
+ blob = find_blob_by_id(image_url.split("/").last)
83
+ return blob if blob.present?
84
+
82
85
  blob = find_blob_by_key(image_url.split("/").second_to_last)
83
86
  return blob if blob.present?
84
87
  end
@@ -120,6 +123,10 @@ module Decidim
120
123
  ActiveStorage::Blob.find_signed(blob_key) if blob_key.present?
121
124
  end
122
125
 
126
+ def find_blob_by_id(blob_id)
127
+ ActiveStorage::Blob.find_by(id: blob_id) if blob_id.present?
128
+ end
129
+
123
130
  def blob_from_attached_file(file)
124
131
  return unless file.respond_to?(:attached?) && file.attached?
125
132
 
@@ -8,7 +8,7 @@ module Decidim
8
8
  end
9
9
 
10
10
  def content_type_allowlist
11
- extension_allowlist.map { |ext| MiniMime.lookup_by_extension(ext).content_type }.uniq
11
+ extension_allowlist.filter_map { |ext| MiniMime.lookup_by_extension(ext)&.content_type }.uniq
12
12
  end
13
13
 
14
14
  # Fetches info about different variants, their processors and dimensions
@@ -0,0 +1,6 @@
1
+ <p class="email-greeting"><%= t(".hello", username: @user_name) %></p>
2
+
3
+ <p class="email-instructions"><%= t ".body_1" %></p>
4
+ <p class="email-instructions"><%= t ".body_2" %></p>
5
+
6
+ <p class="email-closing"><%= t(".greetings_html", organization_name: organization_name(@organization), organization_url: decidim.root_url(host: @organization.host)) %></p>
@@ -1,21 +1,13 @@
1
- <div id="messageErrorModal" data-reveal>
2
- <div>
3
- <h2><%= error %></h2>
4
- <button class="close-button" data-close aria-label="<%= t(".close") %>"
5
- type="button">
6
- <span aria-hidden="true">&times;</span>
7
- </button>
8
- </div>
9
- <div class="reveal__body">
10
- <p><%= t(".intro") %></p>
11
- <p>
12
- <%= messages.join("<br>") %>
13
- </p>
14
- <p><%= t(".correct_errors") %></p>
15
- </div>
16
- <div>
17
- <div>
18
- <a class="button" role="button" href="#" data-close aria-label="<%= t(".ok") %>"><%= t(".ok") %></a>
1
+ <%= decidim_modal id: "messageErrorModal", class: "conversation__modal-error" do %>
2
+ <div data-dialog-container>
3
+ <h2 id="dialog-title-messageErrorModal" tabindex="-1" data-dialog-title><%= error %></h2>
4
+ <div id="dialog-desc-messageErrorModal" class="mt-4">
5
+ <p><%= t(".intro") %></p>
6
+ <p><%= safe_join(messages, tag.br) %></p>
7
+ <p><%= t(".correct_errors") %></p>
8
+ </div>
9
+ <div data-dialog-actions>
10
+ <button type="button" class="button button__sm lg:button__lg button__secondary" data-dialog-close="messageErrorModal" aria-label="<%= t(".ok") %>"><%= t(".ok") %></button>
19
11
  </div>
20
12
  </div>
21
- </div>
13
+ <% end %>
@@ -1,10 +1,15 @@
1
- var $messageError = $("#messageErrorModal");
2
- if ($messageError.length > 0) {
3
- $messageError.foundation("_destroy").remove();
1
+ var messageError = document.getElementById("messageErrorModal")
2
+
3
+ if (messageError) {
4
+ messageError.remove();
4
5
  }
6
+ document.body.insertAdjacentHTML("beforeend", "<%= j(render "error_modal", error:, messages:).html_safe %>");
5
7
 
6
- $("#messages").append("<%= j(render "error_modal", error:, messages:).html_safe %>");
7
- $messageError = $("#messageErrorModal");
8
+ messageError = document.getElementById("messageErrorModal")
9
+ if (messageError) {
10
+ window.createDialog(messageError);
8
11
 
9
- var messageErrorReveal = new Foundation.Reveal($messageError);
10
- messageErrorReveal.open();
12
+ if (Decidim.currentDialogs.messageErrorModal) {
13
+ window.Decidim.currentDialogs.messageErrorModal.open();
14
+ }
15
+ }
@@ -16,7 +16,7 @@
16
16
  </button>
17
17
  <% end %>
18
18
  </div>
19
- <button id="dropdown-trigger-search" data-controller="dropdown" data-target="dropdown-menu-search" data-auto-close="true" aria-expanded="true">
19
+ <button id="dropdown-trigger-search" data-controller="dropdown" data-target="dropdown-menu-search" data-auto-close="true">
20
20
  <%= content_tag :span, t("decidim.searches.filters_small_view.filter_by"), class: "#{"is-active" if params.dig(:filter, :with_resource_type) == nil}" %>
21
21
  <% @blocks.each do |elements| %>
22
22
  <% elements.each do |type, results| %>