Skip to content

Commit 5a77b44

Browse files
authored
fix: popup window size (#744)
1 parent 1a51a25 commit 5a77b44

File tree

1 file changed

+295
-0
lines changed

1 file changed

+295
-0
lines changed
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
/*global SelectBox, interpolate*/
2+
// Handles related-objects functionality: lookup link for raw_id_fields
3+
// and Add Another links.
4+
"use strict";
5+
{
6+
const $ = django.jQuery;
7+
let popupIndex = 0;
8+
const relatedWindows = [];
9+
10+
function dismissChildPopups() {
11+
relatedWindows.forEach(function (win) {
12+
if (!win.closed) {
13+
win.dismissChildPopups();
14+
win.close();
15+
}
16+
});
17+
}
18+
19+
function setPopupIndex() {
20+
if (document.getElementsByName("_popup").length > 0) {
21+
const index = window.name.lastIndexOf("__") + 2;
22+
popupIndex = parseInt(window.name.substring(index));
23+
} else {
24+
popupIndex = 0;
25+
}
26+
}
27+
28+
function addPopupIndex(name) {
29+
return name + "__" + (popupIndex + 1);
30+
}
31+
32+
function removePopupIndex(name) {
33+
return name.replace(new RegExp("__" + (popupIndex + 1) + "$"), "");
34+
}
35+
36+
function showAdminPopup(triggeringLink, name_regexp, add_popup) {
37+
const name = addPopupIndex(triggeringLink.id.replace(name_regexp, ""));
38+
const href = new URL(triggeringLink.href);
39+
if (add_popup) {
40+
href.searchParams.set("_popup", 1);
41+
}
42+
const win = window.open(
43+
href,
44+
name,
45+
"height=768,width=1024,resizable=yes,scrollbars=yes"
46+
);
47+
relatedWindows.push(win);
48+
win.focus();
49+
return false;
50+
}
51+
52+
function showRelatedObjectLookupPopup(triggeringLink) {
53+
return showAdminPopup(triggeringLink, /^lookup_/, true);
54+
}
55+
56+
function dismissRelatedLookupPopup(win, chosenId) {
57+
const name = removePopupIndex(win.name);
58+
const elem = document.getElementById(name);
59+
if (elem.classList.contains("vManyToManyRawIdAdminField") && elem.value) {
60+
elem.value += "," + chosenId;
61+
} else {
62+
document.getElementById(name).value = chosenId;
63+
}
64+
const index = relatedWindows.indexOf(win);
65+
if (index > -1) {
66+
relatedWindows.splice(index, 1);
67+
}
68+
win.close();
69+
}
70+
71+
function showRelatedObjectPopup(triggeringLink) {
72+
return showAdminPopup(triggeringLink, /^(change|add|delete)_/, false);
73+
}
74+
75+
function updateRelatedObjectLinks(triggeringLink) {
76+
const $this = $(triggeringLink);
77+
const siblings = $this.nextAll(
78+
".view-related, .change-related, .delete-related"
79+
);
80+
if (!siblings.length) {
81+
return;
82+
}
83+
const value = $this.val();
84+
if (value) {
85+
siblings.each(function () {
86+
const elm = $(this);
87+
elm.attr(
88+
"href",
89+
elm.attr("data-href-template").replace("__fk__", value)
90+
);
91+
elm.removeAttr("aria-disabled");
92+
});
93+
} else {
94+
siblings.removeAttr("href");
95+
siblings.attr("aria-disabled", true);
96+
}
97+
}
98+
99+
function updateRelatedSelectsOptions(
100+
currentSelect,
101+
win,
102+
objId,
103+
newRepr,
104+
newId,
105+
skipIds = []
106+
) {
107+
// After create/edit a model from the options next to the current
108+
// select (+ or :pencil:) update ForeignKey PK of the rest of selects
109+
// in the page.
110+
111+
const path = win.location.pathname;
112+
// Extract the model from the popup url '.../<model>/add/' or
113+
// '.../<model>/<id>/change/' depending the action (add or change).
114+
const modelName = path.split("/")[path.split("/").length - (objId ? 4 : 3)];
115+
// Select elements with a specific model reference and context of "available-source".
116+
const selectsRelated = document.querySelectorAll(
117+
`[data-model-ref="${modelName}"] [data-context="available-source"]`
118+
);
119+
120+
selectsRelated.forEach(function (select) {
121+
if (
122+
currentSelect === select ||
123+
(skipIds && skipIds.includes(select.id))
124+
) {
125+
return;
126+
}
127+
128+
let option = select.querySelector(`option[value="${objId}"]`);
129+
130+
if (!option) {
131+
option = new Option(newRepr, newId);
132+
select.options.add(option);
133+
// Update SelectBox cache for related fields.
134+
if (
135+
window.SelectBox !== undefined &&
136+
!SelectBox.cache[currentSelect.id]
137+
) {
138+
SelectBox.add_to_cache(select.id, option);
139+
SelectBox.redisplay(select.id);
140+
}
141+
return;
142+
}
143+
144+
option.textContent = newRepr;
145+
option.value = newId;
146+
});
147+
}
148+
149+
function dismissAddRelatedObjectPopup(win, newId, newRepr) {
150+
const name = removePopupIndex(win.name);
151+
const elem = document.getElementById(name);
152+
if (elem) {
153+
const elemName = elem.nodeName.toUpperCase();
154+
if (elemName === "SELECT") {
155+
elem.options[elem.options.length] = new Option(
156+
newRepr,
157+
newId,
158+
true,
159+
true
160+
);
161+
updateRelatedSelectsOptions(elem, win, null, newRepr, newId);
162+
} else if (elemName === "INPUT") {
163+
if (
164+
elem.classList.contains("vManyToManyRawIdAdminField") &&
165+
elem.value
166+
) {
167+
elem.value += "," + newId;
168+
} else {
169+
elem.value = newId;
170+
}
171+
}
172+
// Trigger a change event to update related links if required.
173+
$(elem).trigger("change");
174+
} else {
175+
const toId = name + "_to";
176+
const toElem = document.getElementById(toId);
177+
const o = new Option(newRepr, newId);
178+
SelectBox.add_to_cache(toId, o);
179+
SelectBox.redisplay(toId);
180+
if (toElem && toElem.nodeName.toUpperCase() === "SELECT") {
181+
const skipIds = [name + "_from"];
182+
updateRelatedSelectsOptions(toElem, win, null, newRepr, newId, skipIds);
183+
}
184+
}
185+
const index = relatedWindows.indexOf(win);
186+
if (index > -1) {
187+
relatedWindows.splice(index, 1);
188+
}
189+
win.close();
190+
}
191+
192+
function dismissChangeRelatedObjectPopup(win, objId, newRepr, newId) {
193+
const id = removePopupIndex(win.name.replace(/^edit_/, ""));
194+
const selectsSelector = interpolate("#%s, #%s_from, #%s_to", [id, id, id]);
195+
const selects = $(selectsSelector);
196+
selects
197+
.find("option")
198+
.each(function () {
199+
if (this.value === objId) {
200+
this.textContent = newRepr;
201+
this.value = newId;
202+
}
203+
})
204+
.trigger("change");
205+
updateRelatedSelectsOptions(selects[0], win, objId, newRepr, newId);
206+
selects
207+
.next()
208+
.find(".select2-selection__rendered")
209+
.each(function () {
210+
// The element can have a clear button as a child.
211+
// Use the lastChild to modify only the displayed value.
212+
this.lastChild.textContent = newRepr;
213+
this.title = newRepr;
214+
});
215+
const index = relatedWindows.indexOf(win);
216+
if (index > -1) {
217+
relatedWindows.splice(index, 1);
218+
}
219+
win.close();
220+
}
221+
222+
function dismissDeleteRelatedObjectPopup(win, objId) {
223+
const id = removePopupIndex(win.name.replace(/^delete_/, ""));
224+
const selectsSelector = interpolate("#%s, #%s_from, #%s_to", [id, id, id]);
225+
const selects = $(selectsSelector);
226+
selects
227+
.find("option")
228+
.each(function () {
229+
if (this.value === objId) {
230+
$(this).remove();
231+
}
232+
})
233+
.trigger("change");
234+
const index = relatedWindows.indexOf(win);
235+
if (index > -1) {
236+
relatedWindows.splice(index, 1);
237+
}
238+
win.close();
239+
}
240+
241+
window.showRelatedObjectLookupPopup = showRelatedObjectLookupPopup;
242+
window.dismissRelatedLookupPopup = dismissRelatedLookupPopup;
243+
window.showRelatedObjectPopup = showRelatedObjectPopup;
244+
window.updateRelatedObjectLinks = updateRelatedObjectLinks;
245+
window.dismissAddRelatedObjectPopup = dismissAddRelatedObjectPopup;
246+
window.dismissChangeRelatedObjectPopup = dismissChangeRelatedObjectPopup;
247+
window.dismissDeleteRelatedObjectPopup = dismissDeleteRelatedObjectPopup;
248+
window.dismissChildPopups = dismissChildPopups;
249+
250+
// Kept for backward compatibility
251+
window.showAddAnotherPopup = showRelatedObjectPopup;
252+
window.dismissAddAnotherPopup = dismissAddRelatedObjectPopup;
253+
254+
window.addEventListener("unload", function (evt) {
255+
window.dismissChildPopups();
256+
});
257+
258+
$(document).ready(function () {
259+
setPopupIndex();
260+
$("a[data-popup-opener]").on("click", function (event) {
261+
event.preventDefault();
262+
opener.dismissRelatedLookupPopup(window, $(this).data("popup-opener"));
263+
});
264+
$("body").on(
265+
"click",
266+
'.related-widget-wrapper-link[data-popup="yes"]',
267+
function (e) {
268+
e.preventDefault();
269+
if (this.href) {
270+
const event = $.Event("django:show-related", { href: this.href });
271+
$(this).trigger(event);
272+
if (!event.isDefaultPrevented()) {
273+
showRelatedObjectPopup(this);
274+
}
275+
}
276+
}
277+
);
278+
$("body").on("change", ".related-widget-wrapper select", function (e) {
279+
const event = $.Event("django:update-related");
280+
$(this).trigger(event);
281+
if (!event.isDefaultPrevented()) {
282+
updateRelatedObjectLinks(this);
283+
}
284+
});
285+
$(".related-widget-wrapper select").trigger("change");
286+
$("body").on("click", ".related-lookup", function (e) {
287+
e.preventDefault();
288+
const event = $.Event("django:lookup-related");
289+
$(this).trigger(event);
290+
if (!event.isDefaultPrevented()) {
291+
showRelatedObjectLookupPopup(this);
292+
}
293+
});
294+
});
295+
}

0 commit comments

Comments
 (0)