decidim-core 0.29.4 → 0.29.5
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.
- checksums.yaml +4 -4
- data/app/cells/decidim/activity/show.erb +6 -6
- data/app/cells/decidim/content_blocks/participatory_space_main_data/title.erb +11 -2
- data/app/cells/decidim/footer_topics/show.erb +2 -2
- data/app/cells/decidim/group_admins/show.erb +3 -1
- data/app/cells/decidim/group_members/show.erb +6 -2
- data/app/cells/decidim/images_panel/show.erb +5 -2
- data/app/cells/decidim/report_button/flag_modal.erb +11 -9
- data/app/cells/decidim/report_user_button/flag_modal.erb +11 -10
- data/app/cells/decidim/upload_modal/files.erb +4 -4
- data/app/cells/decidim/upload_modal_cell.rb +5 -3
- data/app/commands/decidim/amendable/accept.rb +2 -1
- data/app/controllers/concerns/decidim/headers/browser_feature_permissions.rb +50 -0
- data/app/controllers/decidim/amendments_controller.rb +3 -3
- data/app/controllers/decidim/application_controller.rb +1 -0
- data/app/helpers/decidim/amendments_helper.rb +2 -1
- data/app/helpers/decidim/filters_helper.rb +25 -0
- data/app/helpers/decidim/layout_helper.rb +6 -0
- data/app/packs/images/decidim/default-avatar.svg +1 -1
- data/app/packs/src/decidim/callout.js +13 -8
- data/app/packs/src/decidim/confirm.js +79 -59
- data/app/packs/src/decidim/datepicker/generate_datepicker.js +2 -0
- data/app/packs/src/decidim/datepicker/generate_timepicker.js +2 -0
- data/app/packs/src/decidim/direct_uploads/upload_field.js +3 -4
- data/app/packs/src/decidim/direct_uploads/upload_modal.js +8 -9
- data/app/packs/src/decidim/dropdown_menu.js +18 -0
- data/app/packs/src/decidim/editor/common/suggestion.js +11 -1
- data/app/packs/src/decidim/form_remote.js +1 -1
- data/app/packs/src/decidim/impersonation.js +1 -1
- data/app/packs/src/decidim/index.js +5 -1
- data/app/packs/src/decidim/session_timeouter.js +1 -1
- data/app/packs/src/decidim/utilities/dom.js +148 -0
- data/app/packs/stylesheets/decidim/_activity.scss +4 -4
- data/app/packs/stylesheets/decidim/_cards.scss +5 -1
- data/app/packs/stylesheets/decidim/_filters.scss +1 -1
- data/app/packs/stylesheets/decidim/_header.scss +11 -3
- data/app/packs/stylesheets/decidim/_layout.scss +2 -2
- data/app/packs/stylesheets/decidim/_modal.scss +1 -5
- data/app/packs/stylesheets/decidim/_modal_update.scss +5 -1
- data/app/permissions/decidim/permissions.rb +13 -1
- data/app/views/decidim/errors/internal_server_error.html.erb +1 -1
- data/app/views/decidim/errors/not_found.html.erb +1 -1
- data/app/views/decidim/newsletters/unsubscribe.html.erb +16 -4
- data/app/views/decidim/searches/_filters.html.erb +48 -13
- data/app/views/decidim/shared/_component_announcement.html.erb +1 -1
- data/app/views/decidim/shared/_confirm_modal.html.erb +3 -5
- data/app/views/decidim/shared/_filters.html.erb +6 -4
- data/app/views/layouts/decidim/_js_configuration.html.erb +1 -0
- data/app/views/layouts/decidim/footer/_main.html.erb +1 -1
- data/app/views/layouts/decidim/footer/_main_intro.html.erb +1 -1
- data/app/views/layouts/decidim/footer/_mini.html.erb +2 -2
- data/app/views/layouts/decidim/header/_main.html.erb +2 -2
- data/app/views/layouts/decidim/header/_main_links_desktop.html.erb +6 -0
- data/app/views/layouts/decidim/header/_main_links_dropdown.html.erb +2 -0
- data/app/views/layouts/decidim/header/_main_links_mobile_account.html.erb +1 -1
- data/app/views/layouts/decidim/header/_mobile_language_choose.html.erb +1 -1
- data/config/locales/ar.yml +0 -3
- data/config/locales/bg-BG.yml +2 -2
- data/config/locales/bg.yml +0 -5
- data/config/locales/ca-IT.yml +29 -5
- data/config/locales/ca.yml +29 -5
- data/config/locales/cs.yml +15 -6
- data/config/locales/de.yml +47 -23
- data/config/locales/el.yml +0 -4
- data/config/locales/en.yml +29 -5
- data/config/locales/es-MX.yml +29 -5
- data/config/locales/es-PY.yml +29 -5
- data/config/locales/es.yml +29 -5
- data/config/locales/eu.yml +63 -39
- data/config/locales/fi-plain.yml +58 -5
- data/config/locales/fi.yml +59 -6
- data/config/locales/fr-CA.yml +29 -5
- data/config/locales/fr-LU.yml +3 -3
- data/config/locales/fr.yml +29 -5
- data/config/locales/gl.yml +0 -3
- data/config/locales/hu.yml +0 -5
- data/config/locales/id-ID.yml +0 -3
- data/config/locales/is-IS.yml +0 -1
- data/config/locales/it.yml +124 -3
- data/config/locales/ja.yml +29 -5
- data/config/locales/lb-LU.yml +2 -2
- data/config/locales/lb.yml +0 -3
- data/config/locales/lt.yml +0 -5
- data/config/locales/lv.yml +0 -3
- data/config/locales/nl.yml +0 -3
- data/config/locales/no.yml +0 -3
- data/config/locales/pl.yml +0 -5
- data/config/locales/pt-BR.yml +1 -4
- data/config/locales/pt.yml +0 -3
- data/config/locales/ro-RO.yml +0 -4
- data/config/locales/ru.yml +0 -3
- data/config/locales/sk-SK.yml +3 -3
- data/config/locales/sk.yml +2 -3
- data/config/locales/sv.yml +28 -4
- data/config/locales/tr-TR.yml +0 -3
- data/config/locales/uk.yml +0 -2
- data/config/locales/zh-CN.yml +0 -3
- data/config/locales/zh-TW.yml +0 -5
- data/lib/decidim/assets/tailwind/tailwind.config.js.erb +2 -1
- data/lib/decidim/core/test/factories.rb +2 -2
- data/lib/decidim/core/test/shared_examples/announcements_examples.rb +4 -0
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/form_builder.rb +14 -0
- metadata +9 -6
@@ -1,5 +1,4 @@
|
|
1
1
|
import UploadModal from "src/decidim/direct_uploads/upload_modal";
|
2
|
-
import { truncateFilename } from "src/decidim/direct_uploads/upload_utility";
|
3
2
|
import { escapeHtml, escapeQuotes } from "src/decidim/utilities/text";
|
4
3
|
|
5
4
|
const updateModalTitle = (modal) => {
|
@@ -30,7 +29,7 @@ const updateActiveUploads = (modal) => {
|
|
30
29
|
const [removeFiles, addFiles] = [modal.items.filter(({ removable }) => removable), modal.items.filter(({ removable }) => !removable)]
|
31
30
|
|
32
31
|
addFiles.forEach((file, ix) => {
|
33
|
-
let title =
|
32
|
+
let title = file.name
|
34
33
|
|
35
34
|
let hidden = ""
|
36
35
|
if (file.hiddenField) {
|
@@ -79,8 +78,8 @@ const updateActiveUploads = (modal) => {
|
|
79
78
|
|
80
79
|
const template = `
|
81
80
|
<div ${attachmentIdOrHiddenField} data-filename="${escapeQuotes(file.name)}" data-title="${escapeQuotes(title)}">
|
82
|
-
${(/image/).test(file.type) &&
|
83
|
-
<span>${escapeHtml(title)}
|
81
|
+
${(/image/).test(file.type) && "<div><img src=\"data:,\" role=\"presentation\" /></div>" || ""}
|
82
|
+
<span>${escapeHtml(title)}</span>
|
84
83
|
${hidden}
|
85
84
|
</div>
|
86
85
|
`
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import { Uploader } from "src/decidim/direct_uploads/uploader";
|
2
2
|
import icon from "src/decidim/icon";
|
3
|
-
import { truncateFilename } from "src/decidim/direct_uploads/upload_utility";
|
4
3
|
import { escapeHtml, escapeQuotes } from "src/decidim/utilities/text";
|
5
4
|
|
6
5
|
const STATUS = {
|
@@ -42,7 +41,7 @@ export default class UploadModal {
|
|
42
41
|
|
43
42
|
this.emptyItems = this.modal.querySelector("[data-dropzone-no-items]");
|
44
43
|
this.uploadItems = this.modal.querySelector("[data-dropzone-items]");
|
45
|
-
this.input = this.dropZone.querySelector("input");
|
44
|
+
this.input = this.dropZone.querySelector("input[type=file]");
|
46
45
|
this.items = []
|
47
46
|
|
48
47
|
this.attachmentCounter = 0;
|
@@ -171,29 +170,29 @@ export default class UploadModal {
|
|
171
170
|
|
172
171
|
createUploadItem(file, errors, opts = {}) {
|
173
172
|
const okTemplate = `
|
174
|
-
<img src="data:,"
|
175
|
-
<span>${escapeHtml(
|
173
|
+
<img src="data:,", role="presentation" />
|
174
|
+
<span class="upload-modal__span">${escapeHtml(file.name)}</span>
|
176
175
|
`
|
177
176
|
|
178
177
|
const errorTemplate = `
|
179
178
|
<div>${icon("error-warning-line")}</div>
|
180
179
|
<div>
|
181
|
-
<span>${escapeHtml(
|
180
|
+
<span class="upload-modal__span">${escapeHtml(file.name)}</span>
|
182
181
|
<span>${this.locales.validation_error}</span>
|
183
182
|
<ul>${errors.map((error) => `<li>${error}</li>`).join("\n")}</ul>
|
184
183
|
</div>
|
185
184
|
`
|
186
185
|
|
187
186
|
const titleTemplate = `
|
188
|
-
<img src="data:,"
|
187
|
+
<img src="data:," role="presentation" />
|
189
188
|
<div>
|
190
189
|
<div>
|
191
190
|
<label>${this.locales.filename}</label>
|
192
|
-
<span>${escapeHtml(
|
191
|
+
<span class="upload-modal__span">${escapeHtml(file.name)}</span>
|
193
192
|
</div>
|
194
193
|
<div>
|
195
|
-
<label>${this.locales.title}</label>
|
196
|
-
<input class="sm" type="text" value="${escapeQuotes(opts.title ||
|
194
|
+
<label for="${file.name}">${this.locales.title}</label>
|
195
|
+
<input class="sm" type="text" value="${escapeQuotes(opts.title || file.name)}" id="${file.name}" />
|
197
196
|
</div>
|
198
197
|
</div>
|
199
198
|
`
|
@@ -0,0 +1,18 @@
|
|
1
|
+
// changes the value "menu" of role attribute set by a11y on div dropdown-menu-account and
|
2
|
+
// dropdown-menu-account-mobile which are inappropriate for accessibility
|
3
|
+
document.addEventListener("DOMContentLoaded", () => {
|
4
|
+
const dropdownDiv = document.querySelector("#dropdown-menu-account");
|
5
|
+
const dropdownMobileDiv = document.querySelector("#dropdown-menu-account-mobile");
|
6
|
+
if (dropdownDiv) {
|
7
|
+
setTimeout(() => {
|
8
|
+
dropdownDiv.setAttribute("role", "dialog")
|
9
|
+
dropdownMobileDiv.setAttribute("role", "dialog")
|
10
|
+
}, 300)
|
11
|
+
}
|
12
|
+
const triggerButtonMobile = document.querySelector("#dropdown-trigger-links-mobile");
|
13
|
+
if (triggerButtonMobile) {
|
14
|
+
triggerButtonMobile.addEventListener("click", () => {
|
15
|
+
dropdownMobileDiv.setAttribute("aria-modal", "true")
|
16
|
+
})
|
17
|
+
}
|
18
|
+
});
|
@@ -98,7 +98,17 @@ export const createSuggestionRenderer = (node, { itemConverter } = {}) => () =>
|
|
98
98
|
suggestionItem.textContent = label;
|
99
99
|
suggestion.append(suggestionItem);
|
100
100
|
|
101
|
-
suggestionItem.addEventListener("click", () =>
|
101
|
+
suggestionItem.addEventListener("click", (ev) => {
|
102
|
+
ev.preventDefault();
|
103
|
+
|
104
|
+
if (currentRange) {
|
105
|
+
// Increase the current range by 1 since the ENTER key would do the
|
106
|
+
// same when doing the selection through keyboard.
|
107
|
+
currentRange.to += 1;
|
108
|
+
}
|
109
|
+
|
110
|
+
selectItem(idx);
|
111
|
+
});
|
102
112
|
});
|
103
113
|
}
|
104
114
|
|
@@ -15,7 +15,7 @@ $(() => {
|
|
15
15
|
}, 1000);
|
16
16
|
|
17
17
|
// Prevent reload when page is already unloading, otherwise it may cause infinite reloads.
|
18
|
-
window.addEventListener("
|
18
|
+
window.addEventListener("pagehide", () => {
|
19
19
|
clearInterval(exitInterval);
|
20
20
|
return;
|
21
21
|
});
|
@@ -41,7 +41,6 @@ import "src/decidim/vizzs"
|
|
41
41
|
import "src/decidim/responsive_horizontal_tabs"
|
42
42
|
import "src/decidim/security/selfxss_warning"
|
43
43
|
import "src/decidim/session_timeouter"
|
44
|
-
import "src/decidim/confirm"
|
45
44
|
import "src/decidim/results_listing"
|
46
45
|
import "src/decidim/impersonation"
|
47
46
|
import "src/decidim/gallery"
|
@@ -51,8 +50,10 @@ import "src/decidim/abide_form_validator_fixer"
|
|
51
50
|
import "src/decidim/sw"
|
52
51
|
import "src/decidim/sticky_header"
|
53
52
|
import "src/decidim/attachments"
|
53
|
+
import "src/decidim/dropdown_menu"
|
54
54
|
|
55
55
|
// local deps that require initialization
|
56
|
+
import ConfirmDialog, { initializeConfirm } from "src/decidim/confirm"
|
56
57
|
import formDatePicker from "src/decidim/datepicker/form_datepicker"
|
57
58
|
import Configuration from "src/decidim/configuration"
|
58
59
|
import ExternalLink from "src/decidim/external_link"
|
@@ -91,6 +92,7 @@ window.Decidim = window.Decidim || {
|
|
91
92
|
addInputEmoji,
|
92
93
|
EmojiButton,
|
93
94
|
Dialogs,
|
95
|
+
ConfirmDialog,
|
94
96
|
announceForScreenReader
|
95
97
|
};
|
96
98
|
|
@@ -123,6 +125,8 @@ window.initFoundation = (element) => {
|
|
123
125
|
});
|
124
126
|
};
|
125
127
|
|
128
|
+
// Confirm initialization needs to happen before Rails.start()
|
129
|
+
initializeConfirm();
|
126
130
|
Rails.start()
|
127
131
|
|
128
132
|
/**
|
@@ -0,0 +1,148 @@
|
|
1
|
+
import confirmAction from "src/decidim/confirm"
|
2
|
+
import { getMessages } from "src/decidim/i18n"
|
3
|
+
|
4
|
+
const { Rails } = window;
|
5
|
+
|
6
|
+
const createUnloadPreventer = () => {
|
7
|
+
const preventUnloadConditions = [];
|
8
|
+
|
9
|
+
const confirmMessage = getMessages("confirmUnload") || "Are you sure you want to leave this page?";
|
10
|
+
|
11
|
+
const canUnload = (event) => !preventUnloadConditions.some((condition) => condition(event));
|
12
|
+
|
13
|
+
// TLDR:
|
14
|
+
// The beforeunload event does not work during tests due to the deprecation of
|
15
|
+
// the unload event and ChromeDriver automatically accepting these dialogs.
|
16
|
+
// ---
|
17
|
+
//
|
18
|
+
// Even when there are custom listeners on links and forms, the beforeunload
|
19
|
+
// event is to ensure that the user does not accidentally reload the page or
|
20
|
+
// close the browser or the tab. Note that this does not work during the tests
|
21
|
+
// with ChromeDriver due to the deprecation of the unload event and
|
22
|
+
// ChromeDriver automatically accepting these dialogs. For the time being,
|
23
|
+
// this should work when a real user interacts with the browser along with the
|
24
|
+
// "Permissions-Policy" header set by the backend. For more information about
|
25
|
+
// the header, see Decidim::Headers::BrowserFeaturePermissions).
|
26
|
+
const unloadListener = (event) => {
|
27
|
+
if (canUnload(event)) {
|
28
|
+
return;
|
29
|
+
}
|
30
|
+
|
31
|
+
// According to:
|
32
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
|
33
|
+
//
|
34
|
+
// > [...] best practice is to trigger the dialog by invoking
|
35
|
+
// > preventDefault() on the event object, while also setting returnValue to
|
36
|
+
// > support legacy cases.
|
37
|
+
event.preventDefault();
|
38
|
+
event.returnValue = true;
|
39
|
+
};
|
40
|
+
|
41
|
+
// The beforeunload event listener has to be registered AFTER a user
|
42
|
+
// interaction which is why it is wrapped around the next click event that
|
43
|
+
// happens after the first unload listener was registered. Otherwise it might
|
44
|
+
// not work due to the deprecation of the unload APIs in Chromium based
|
45
|
+
// browsers and possibly in the web standards in the future.
|
46
|
+
//
|
47
|
+
// According to:
|
48
|
+
// https://developer.chrome.com/docs/web-platform/page-lifecycle-api#the_beforeunload_event
|
49
|
+
//
|
50
|
+
// > Never add a beforeunload listener unconditionally or use it as an
|
51
|
+
// > end-of-session signal. Only add it when a user has unsaved work, and
|
52
|
+
// > remove it as soon as that work has been saved.
|
53
|
+
const registerBeforeUnload = () => {
|
54
|
+
window.removeEventListener("click", registerBeforeUnload);
|
55
|
+
window.addEventListener("beforeunload", unloadListener);
|
56
|
+
};
|
57
|
+
|
58
|
+
const disableBeforeUnload = () => {
|
59
|
+
window.removeEventListener("click", registerBeforeUnload);
|
60
|
+
window.removeEventListener("beforeunload", unloadListener);
|
61
|
+
};
|
62
|
+
|
63
|
+
const linkClickListener = (ev) => {
|
64
|
+
const link = ev.target?.closest("a");
|
65
|
+
if (!link) {
|
66
|
+
return;
|
67
|
+
}
|
68
|
+
|
69
|
+
if (canUnload(ev)) {
|
70
|
+
disableBeforeUnload();
|
71
|
+
document.removeEventListener("click", linkClickListener);
|
72
|
+
return;
|
73
|
+
}
|
74
|
+
|
75
|
+
window.exitUrl = link.href;
|
76
|
+
|
77
|
+
ev.preventDefault();
|
78
|
+
ev.stopPropagation();
|
79
|
+
|
80
|
+
confirmAction(confirmMessage, link).then((answer) => {
|
81
|
+
if (!answer) {
|
82
|
+
return;
|
83
|
+
}
|
84
|
+
|
85
|
+
disableBeforeUnload();
|
86
|
+
document.removeEventListener("click", linkClickListener);
|
87
|
+
link.click();
|
88
|
+
});
|
89
|
+
};
|
90
|
+
|
91
|
+
const formSubmitListener = (ev) => {
|
92
|
+
const source = ev.target?.closest("form");
|
93
|
+
if (!source) {
|
94
|
+
return;
|
95
|
+
}
|
96
|
+
|
97
|
+
if (canUnload(ev)) {
|
98
|
+
disableBeforeUnload();
|
99
|
+
document.removeEventListener("submit", formSubmitListener);
|
100
|
+
return;
|
101
|
+
}
|
102
|
+
|
103
|
+
const button = source.closest(Rails.formSubmitSelector);
|
104
|
+
if (!button) {
|
105
|
+
return;
|
106
|
+
}
|
107
|
+
|
108
|
+
ev.preventDefault();
|
109
|
+
ev.stopImmediatePropagation();
|
110
|
+
ev.stopPropagation();
|
111
|
+
|
112
|
+
confirmAction(confirmMessage, button).then((answer) => {
|
113
|
+
if (!answer) {
|
114
|
+
return;
|
115
|
+
}
|
116
|
+
|
117
|
+
disableBeforeUnload();
|
118
|
+
document.removeEventListener("submit", formSubmitListener);
|
119
|
+
source.submit();
|
120
|
+
});
|
121
|
+
};
|
122
|
+
|
123
|
+
const registerPreventUnloadListeners = () => {
|
124
|
+
window.addEventListener("click", registerBeforeUnload);
|
125
|
+
document.addEventListener("click", linkClickListener);
|
126
|
+
document.addEventListener("submit", formSubmitListener);
|
127
|
+
};
|
128
|
+
|
129
|
+
return {
|
130
|
+
addPreventCondition: (condition) => {
|
131
|
+
if (typeof condition !== "function") {
|
132
|
+
return;
|
133
|
+
}
|
134
|
+
|
135
|
+
if (preventUnloadConditions.length < 1) {
|
136
|
+
// The unload listeners are global, so only the first call to this
|
137
|
+
// function should result to registering these listeners.
|
138
|
+
registerPreventUnloadListeners();
|
139
|
+
}
|
140
|
+
|
141
|
+
preventUnloadConditions.push(condition);
|
142
|
+
}
|
143
|
+
};
|
144
|
+
};
|
145
|
+
|
146
|
+
const unloadPreventer = createUnloadPreventer();
|
147
|
+
|
148
|
+
export const preventUnload = (condition) => unloadPreventer.addPreventCondition(condition);
|
@@ -12,12 +12,12 @@
|
|
12
12
|
&__content {
|
13
13
|
@apply col-span-2 md:col-span-1 order-first md:order-none space-y-2;
|
14
14
|
|
15
|
-
>
|
16
|
-
|
17
|
-
@apply text-gray-2 text-sm;
|
15
|
+
> div:first-child {
|
16
|
+
p {
|
17
|
+
@apply text-gray-2 text-sm inline-block;
|
18
18
|
|
19
19
|
svg {
|
20
|
-
@apply text-gray fill-current;
|
20
|
+
@apply text-gray fill-current inline;
|
21
21
|
}
|
22
22
|
}
|
23
23
|
|
@@ -159,11 +159,15 @@
|
|
159
159
|
/* shared styles */
|
160
160
|
&__highlight-metadata,
|
161
161
|
&__grid-metadata {
|
162
|
-
@apply mt-auto flex items-center justify-between flex-wrap
|
162
|
+
@apply mt-auto flex items-center justify-between flex-wrap text-sm text-gray-2 [&>*]:flex [&>*]:items-center [&>*]:gap-1 first:[&>*]:flex-none;
|
163
163
|
|
164
164
|
svg {
|
165
165
|
@apply flex-none text-gray fill-current;
|
166
166
|
}
|
167
|
+
|
168
|
+
[data-author] {
|
169
|
+
@apply flex first:[&>*]:max-w-40 -mr-8;
|
170
|
+
}
|
167
171
|
}
|
168
172
|
|
169
173
|
/* shared styles */
|
@@ -61,7 +61,7 @@ header {
|
|
61
61
|
@apply hidden md:block col-span-2 col-start-5 xl:col-start-4;
|
62
62
|
|
63
63
|
form {
|
64
|
-
@apply block relative rounded text-md bg-background;
|
64
|
+
@apply block relative rounded text-md border border-neutral-200 outline outline-1 outline-transparent rounded bg-background-2 leading-relaxed;
|
65
65
|
}
|
66
66
|
|
67
67
|
input[type="text"] {
|
@@ -69,7 +69,7 @@ header {
|
|
69
69
|
}
|
70
70
|
|
71
71
|
button[type="submit"] {
|
72
|
-
@apply absolute ltr:right-2 rtl:left-2 inset-y-
|
72
|
+
@apply absolute ltr:right-2 rtl:left-2 inset-y-0 text-secondary px-2;
|
73
73
|
}
|
74
74
|
}
|
75
75
|
|
@@ -404,7 +404,7 @@ header {
|
|
404
404
|
@apply w-full px-4;
|
405
405
|
|
406
406
|
&-menu {
|
407
|
-
@apply w-full md:w-
|
407
|
+
@apply w-full md:w-[100%] mt-0 grid md:grid-cols-2 gap-x-6 text-secondary;
|
408
408
|
|
409
409
|
> * {
|
410
410
|
@apply py-3 md:py-3.5 border-b last:border-0 border-gray-3;
|
@@ -431,6 +431,14 @@ header {
|
|
431
431
|
|
432
432
|
&__bottom {
|
433
433
|
@apply hidden md:flex;
|
434
|
+
|
435
|
+
&-right {
|
436
|
+
@apply mr-2 mb-2;
|
437
|
+
}
|
438
|
+
|
439
|
+
&-left {
|
440
|
+
@apply mr-2 mt-1;
|
441
|
+
}
|
434
442
|
}
|
435
443
|
|
436
444
|
&__title {
|
@@ -2,11 +2,11 @@
|
|
2
2
|
@apply flex flex-col min-h-screen;
|
3
3
|
|
4
4
|
&__skip {
|
5
|
-
@apply absolute z-
|
5
|
+
@apply absolute z-[9999] left-0 -translate-x-full py-1 px-4 bg-primary rounded-br-lg text-white cursor-pointer transition focus:translate-x-0;
|
6
6
|
}
|
7
7
|
|
8
8
|
[data-content] {
|
9
|
-
@apply relative flex flex-col;
|
9
|
+
@apply relative flex flex-col flex-1;
|
10
10
|
}
|
11
11
|
}
|
12
12
|
|
@@ -18,15 +18,11 @@
|
|
18
18
|
}
|
19
19
|
|
20
20
|
[data-dialog-container] {
|
21
|
-
@apply grid grid-
|
21
|
+
@apply grid grid-rows-[auto_1fr] items-start md:items-center gap-2 text-left;
|
22
22
|
|
23
23
|
> svg {
|
24
24
|
@apply w-6 h-6 text-gray fill-current flex-none;
|
25
25
|
}
|
26
|
-
|
27
|
-
> :last-child {
|
28
|
-
@apply col-span-2 md:col-span-1 md:col-start-2;
|
29
|
-
}
|
30
26
|
}
|
31
27
|
|
32
28
|
[data-dialog-title] {
|
@@ -106,7 +106,19 @@ module Decidim
|
|
106
106
|
return allow! if component.current_settings.amendment_creation_enabled
|
107
107
|
when :accept,
|
108
108
|
:reject
|
109
|
-
return
|
109
|
+
return disallow! unless component.current_settings.amendment_reaction_enabled
|
110
|
+
|
111
|
+
amendable = context.fetch(:amendable, nil)
|
112
|
+
|
113
|
+
if amendable.respond_to?(:official?) && amendable.official?
|
114
|
+
return allow! if user.admin?
|
115
|
+
|
116
|
+
return disallow!
|
117
|
+
end
|
118
|
+
|
119
|
+
return disallow! unless amendable.authored_by?(user)
|
120
|
+
|
121
|
+
return allow!
|
110
122
|
when :promote
|
111
123
|
return allow! if component.current_settings.amendment_promotion_enabled
|
112
124
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%= render layout: "layouts/decidim/shared/layout_center" do %>
|
2
2
|
|
3
|
-
<div class="
|
3
|
+
<div class="flex flex-col items-center">
|
4
4
|
<div class="flex justify-center">
|
5
5
|
<h1 class="title-decorator my-12"><%= t("title", scope: "decidim.errors.internal_server_error") %></h1>
|
6
6
|
</div>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%= render layout: "layouts/decidim/shared/layout_center" do %>
|
2
2
|
|
3
|
-
<div class="
|
3
|
+
<div class="flex flex-col items-center">
|
4
4
|
<div class="flex justify-center">
|
5
5
|
<h1 class="title-decorator my-12"><%= t("title", scope: "decidim.errors.not_found") %></h1>
|
6
6
|
</div>
|
@@ -1,4 +1,16 @@
|
|
1
|
-
<
|
2
|
-
<
|
3
|
-
|
4
|
-
</div>
|
1
|
+
<main class="layout-1col cols-6">
|
2
|
+
<div class="text-center py-12">
|
3
|
+
<h1 class="h1 decorator inline-block text-left"><%= t ".unsubscribe" %></h1>
|
4
|
+
</div>
|
5
|
+
<div class="page__container">
|
6
|
+
<div class="editor-content">
|
7
|
+
<div class="rich-text-display">
|
8
|
+
<%= t ".subscription_preferences", organization_name: translated_attribute(current_organization.name) %>
|
9
|
+
</div>
|
10
|
+
<br>
|
11
|
+
<div class="rich-text-display">
|
12
|
+
<%= t ".check_subscription_html", link: decidim.notifications_settings_path %>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
</main>
|
@@ -12,35 +12,70 @@
|
|
12
12
|
<%= icon "arrow-down-s-line", class: "w-8 h-8 flex-none text-secondary fill-current" %>
|
13
13
|
<%= icon "arrow-up-s-line", class: "w-8 h-8 flex-none text-secondary fill-current" %>
|
14
14
|
</button>
|
15
|
-
<
|
16
|
-
<
|
17
|
-
<%= link_to main_search_path, class: "filter#{" is-active" if params.dig(:filter, :with_resource_type) == nil}" do %>
|
15
|
+
<ul id="dropdown-menu-search" aria-hidden="true">
|
16
|
+
<li>
|
17
|
+
<%= link_to main_search_path, class: "filter#{" is-active" if params.dig(:filter, :with_resource_type) == nil}", "aria-current": params.dig(:filter, :with_resource_type) == nil ? "true" : "" do %>
|
18
18
|
<%= resource_type_icon("all") %>
|
19
19
|
<span><%= t("all", scope: "decidim.searches.filters.state") %></span>
|
20
20
|
<span class="label ml-auto"><%= @results_count %></span>
|
21
21
|
<% end %>
|
22
|
-
</
|
22
|
+
</li>
|
23
23
|
<% @blocks.each do |elements| %>
|
24
|
-
<
|
25
|
-
|
26
|
-
|
24
|
+
<li>
|
25
|
+
<ul>
|
26
|
+
<% elements.each do |type, results| %>
|
27
27
|
<% if results[:count].positive? %>
|
28
|
-
|
28
|
+
<li>
|
29
|
+
<%= link_to search_path_by_resource_type(type), class: "filter#{" is-active" if params.dig(:filter, :with_resource_type) == type}", "aria-current": params.dig(:filter, :with_resource_type) == type ? "true" : "" do %>
|
29
30
|
<%= resource_type_icon(type) %>
|
30
31
|
<span><%= searchable_resource_human_name(type) %></span>
|
31
32
|
<span class="label ml-auto"><%= results[:count] %></span>
|
32
33
|
<% end %>
|
34
|
+
</li>
|
33
35
|
<% else %>
|
34
|
-
<%= content_tag :
|
36
|
+
<%= content_tag :li, class: "filter#{" is-empty" if results[:count].zero?}" do %>
|
35
37
|
<%= resource_type_icon(type) %>
|
36
38
|
<span><%= searchable_resource_human_name(type) %></span>
|
37
39
|
<span class="label ml-auto"><%= results[:count] %></span>
|
38
40
|
<% end %>
|
39
41
|
<% end %>
|
40
|
-
|
41
|
-
|
42
|
-
</
|
42
|
+
<% end %>
|
43
|
+
</ul>
|
44
|
+
</li>
|
43
45
|
<% end %>
|
44
|
-
</
|
46
|
+
</ul>
|
45
47
|
</div>
|
46
48
|
</nav>
|
49
|
+
<script type="text/javascript">
|
50
|
+
const button = document.querySelector('#dropdown-trigger-search');
|
51
|
+
const arrowDown = button.querySelector('svg:first-of-type');
|
52
|
+
const arrowUp = button.querySelector('svg:last-of-type');
|
53
|
+
const list = document.querySelector('#dropdown-menu-search');
|
54
|
+
// the arrow is up when user arrives on the page, with the list displayed
|
55
|
+
arrowUp.addEventListener('click', function(){
|
56
|
+
setTimeout(() => {
|
57
|
+
button.setAttribute('aria-expanded', 'false');
|
58
|
+
list.style.display = "none";
|
59
|
+
}, 300)
|
60
|
+
})
|
61
|
+
arrowDown.addEventListener('click', function(){
|
62
|
+
setTimeout(() => {
|
63
|
+
button.setAttribute('aria-expanded', 'true');
|
64
|
+
list.style.display = "block";
|
65
|
+
}, 300)
|
66
|
+
})
|
67
|
+
// 32 is code for space bar and 13 is code for enter
|
68
|
+
button.addEventListener('keydown', function(e){
|
69
|
+
if ((e.keyCode === 13 || e.keyCode === 32) && button.getAttribute('aria-expanded') === 'true') {
|
70
|
+
setTimeout(() => {
|
71
|
+
button.setAttribute('aria-expanded', 'false');
|
72
|
+
list.style.display = "none";
|
73
|
+
}, 300)
|
74
|
+
} else if ((e.keyCode === 13 || e.keyCode === 32) && button.getAttribute('aria-expanded') === 'false'){
|
75
|
+
setTimeout(() => {
|
76
|
+
button.setAttribute('aria-expanded', 'true');
|
77
|
+
list.style.display = "block";
|
78
|
+
}, 300)
|
79
|
+
}
|
80
|
+
})
|
81
|
+
</script>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<% announcement = translated_attribute(current_settings.announcement).presence || translated_attribute(component_settings.announcement).presence %>
|
2
|
-
<% if announcement %>
|
2
|
+
<% if strip_tags(announcement).present? %>
|
3
3
|
<section class="layout-main__section">
|
4
4
|
<%= cell("decidim/announcement", announcement, local_assigns.merge(callout_class: "editor-content")) %>
|
5
5
|
</section>
|
@@ -1,16 +1,14 @@
|
|
1
1
|
<%= decidim_modal id: "confirm-modal" do %>
|
2
2
|
<div data-dialog-container>
|
3
|
-
<%= icon "delete-bin-line" %>
|
4
3
|
<h2 class="h2" data-dialog-title id="dialog-title-confirm-modal"><%= t("title", scope: "decidim.shared.confirm_modal") %></h2>
|
5
|
-
|
6
4
|
<div data-confirm-modal-content></div>
|
7
5
|
</div>
|
8
6
|
|
9
|
-
<div data-dialog-actions>
|
10
|
-
<button class="button button__lg button__transparent-secondary" data-confirm-cancel data-dialog-close="confirm-modal">
|
7
|
+
<div data-dialog-actions class="flex flex-col-reverse md:flex-row">
|
8
|
+
<button class="button button__lg button__transparent-secondary w-full md:w-auto" data-confirm-cancel data-dialog-close="confirm-modal">
|
11
9
|
<span><%= t("cancel", scope: "decidim.shared.confirm_modal") %></span>
|
12
10
|
</button>
|
13
|
-
<button class="button button__lg button__secondary" data-confirm-ok>
|
11
|
+
<button class="button button__lg button__secondary w-full md:w-auto" data-confirm-ok>
|
14
12
|
<span><%= t("ok", scope: "decidim.shared.confirm_modal") %></span>
|
15
13
|
</button>
|
16
14
|
</div>
|