blacklight 8.0.0.beta6 → 8.0.0.beta8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ccd45650da142937ce72464e061e05ed63793c553e0d0676d139010dd218cb44
4
- data.tar.gz: 5221c9746cf8c4168444c57ca18062be9a6cb95743a58ea53040056ccf8bdee9
3
+ metadata.gz: bf6f2c77b039ffcb18b23c50795dd2acd9dd410e50749422a55a2d6b7f85d9aa
4
+ data.tar.gz: f5edad3ff59994077cbe368027d5af44ed1284cad8d561b8dba46e12a82a0688
5
5
  SHA512:
6
- metadata.gz: 6dc2e36331089bcd0404a3a34c3a8bf71d6fa103c19a65f7e94620e14ac41a5f8ccfe8c1b750a305bbb7db051c323a7410b693a137579073f776e65debd76c5b
7
- data.tar.gz: fb238384ea2c98c6e442b6706c473766118e83e860d3d4509e45b4cd50e993e22da8f67b29d61b4f4a40e6a9502cb8b07f822129ff9f3348f6c5c40cd210ee66
6
+ metadata.gz: 4f8630de40970d3cd1422168e292445057172b55942eebd6fccbda3ca6be8141c89b9981ee459880b8185482d4c37d957803bcc610b87afa612876760085439d
7
+ data.tar.gz: 90e98ba147b335d833ca80b139776b89c622c18184a59e8c19083fa0b651983206ab209aa3b0429a673b34781a9cdfcc53dc6b12bafeaf74e4da575a00cd2151
data/VERSION CHANGED
@@ -1 +1 @@
1
- 8.0.0.beta6
1
+ 8.0.0.beta8
@@ -0,0 +1,376 @@
1
+ /* Converts a "toggle" form, with single submit button to add/remove
2
+ something, like used for Bookmarks, into an AJAXy checkbox instead.
3
+ Apply to a form. Does require certain assumption about the form:
4
+ 1) The same form 'action' href must be used for both ADD and REMOVE
5
+ actions, with the different being the hidden input name="_method"
6
+ being set to "put" or "delete" -- that's the Rails method to pretend
7
+ to be doing a certain HTTP verb. So same URL, PUT to add, DELETE
8
+ to remove. This plugin assumes that.
9
+ Plus, the form this is applied to should provide a data-doc-id
10
+ attribute (HTML5-style doc-*) that contains the id/primary key
11
+ of the object in question -- used by plugin for a unique value for
12
+ DOM id's.
13
+ Uses HTML for a checkbox compatible with Bootstrap 4.
14
+ new CheckboxSubmit(document.querySelector('form.something')).render()
15
+ */
16
+ class CheckboxSubmit {
17
+ constructor(form) {
18
+ this.form = form;
19
+ }
20
+
21
+ async clicked(evt) {
22
+ this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress');
23
+ this.labelTarget.setAttribute('disabled', 'disabled');
24
+ this.checkboxTarget.setAttribute('disabled', 'disabled');
25
+ const response = await fetch(this.formTarget.getAttribute('action'), {
26
+ body: new FormData(this.formTarget),
27
+ method: this.formTarget.getAttribute('method').toUpperCase(),
28
+ headers: {
29
+ 'Accept': 'application/json',
30
+ 'X-Requested-With': 'XMLHttpRequest',
31
+ 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content
32
+ }
33
+ });
34
+ this.labelTarget.removeAttribute('disabled');
35
+ this.checkboxTarget.removeAttribute('disabled');
36
+ if (response.ok) {
37
+ const json = await response.json();
38
+ this.updateStateFor(!this.checked);
39
+ document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count;
40
+ } else {
41
+ alert('Error');
42
+ }
43
+ }
44
+
45
+ get checked() {
46
+ return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)
47
+ }
48
+
49
+ get formTarget() {
50
+ return this.form
51
+ }
52
+
53
+ get labelTarget() {
54
+ return this.form.querySelector('[data-checkboxsubmit-target="label"]')
55
+ }
56
+
57
+ get checkboxTarget() {
58
+ return this.form.querySelector('[data-checkboxsubmit-target="checkbox"]')
59
+ }
60
+
61
+ get spanTarget() {
62
+ return this.form.querySelector('[data-checkboxsubmit-target="span"]')
63
+ }
64
+
65
+ updateStateFor(state) {
66
+ this.checkboxTarget.checked = state;
67
+
68
+ if (state) {
69
+ this.labelTarget.classList.add('checked');
70
+ //Set the Rails hidden field that fakes an HTTP verb
71
+ //properly for current state action.
72
+ this.formTarget.querySelector('input[name=_method]').value = 'delete';
73
+ this.spanTarget.innerHTML = this.form.getAttribute('data-present');
74
+ } else {
75
+ this.labelTarget.classList.remove('checked');
76
+ this.formTarget.querySelector('input[name=_method]').value = 'put';
77
+ this.spanTarget.innerHTML = this.form.getAttribute('data-absent');
78
+ }
79
+ }
80
+ }
81
+
82
+ const BookmarkToggle = (e) => {
83
+ if (e.target.matches('[data-checkboxsubmit-target="checkbox"]')) {
84
+ const form = e.target.closest('form');
85
+ if (form) new CheckboxSubmit(form).clicked(e);
86
+ }
87
+ };
88
+
89
+ document.addEventListener('click', BookmarkToggle);
90
+
91
+ const ButtonFocus = (e) => {
92
+ // Button clicks should change focus. As of 10/3/19, Firefox for Mac and
93
+ // Safari both do not set focus to a button on button click.
94
+ // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information
95
+ if (e.target.matches('[data-toggle="collapse"]') || e.target.matches('[data-bs-toggle="collapse"]')) {
96
+ e.target.focus();
97
+ }
98
+ };
99
+
100
+ document.addEventListener('click', ButtonFocus);
101
+
102
+ /*
103
+ The blacklight modal plugin can display some interactions inside a Bootstrap
104
+ modal window, including some multi-page interactions.
105
+
106
+ It supports unobtrusive Javascript, where a link or form that would have caused
107
+ a new page load is changed to display it's results inside a modal dialog,
108
+ by this plugin. The plugin assumes there is a Bootstrap modal div
109
+ on the page with id #blacklight-modal to use as the modal -- the standard Blacklight
110
+ layout provides this.
111
+
112
+ To make a link or form have their results display inside a modal, add
113
+ `data-blacklight-modal="trigger"` to the link or form. (Note, form itself not submit input)
114
+ With Rails link_to helper, you'd do that like:
115
+
116
+ link_to something, link, data: { blacklight_modal: "trigger" }
117
+
118
+ The results of the link href or form submit will be displayed inside
119
+ a modal -- they should include the proper HTML markup for a bootstrap modal's
120
+ contents. Also, you ordinarily won't want the Rails template with wrapping
121
+ navigational elements to be used. The Rails controller could suppress
122
+ the layout when a JS AJAX request is detected, OR the response
123
+ can include a `<div data-blacklight-modal="container">` -- only the contents
124
+ of the container will be placed inside the modal, the rest of the
125
+ page will be ignored.
126
+
127
+ Link or forms inside the modal will ordinarily cause page loads
128
+ when they are triggered. However, if you'd like their results
129
+ to stay within the modal, just add `data-blacklight-modal="preserve"`
130
+ to the link or form.
131
+
132
+ Here's an example of what might be returned, demonstrating most of the devices available:
133
+
134
+ <div data-blacklight-modal="container">
135
+ <div class="modal-header">
136
+ <button type="button" class="close" data-bl-dismiss="modal" aria-hidden="true">×</button>
137
+ <h3 class="modal-title">Request Placed</h3>
138
+ </div>
139
+
140
+ <div class="modal-body">
141
+ <p>Some message</p>
142
+ <%= link_to "This result will still be within modal", some_link, data: { blacklight_modal: "preserve" } %>
143
+ </div>
144
+
145
+
146
+ <div class="modal-footer">
147
+ <button type="button" class="btn btn-secondary" data-bl-dismiss="modal">Close</button>
148
+ </div>
149
+ </div>
150
+
151
+
152
+ One additional feature. If the content returned from the AJAX form submission
153
+ can be a turbo-stream that defines some HTML fragementsand where on the page to put them:
154
+ https://turbo.hotwired.dev/handbook/streams
155
+ */
156
+
157
+ const Modal = (() => {
158
+ const modal = {};
159
+
160
+ // a Bootstrap modal div that should be already on the page hidden
161
+ modal.modalSelector = '#blacklight-modal';
162
+
163
+ // Trigger selectors identify forms or hyperlinks that should open
164
+ // inside a modal dialog.
165
+ modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';
166
+
167
+ // preserve selectors identify forms or hyperlinks that, if activated already
168
+ // inside a modal dialog, should have destinations remain inside the modal -- but
169
+ // won't trigger a modal if not already in one.
170
+ //
171
+ // No need to repeat selectors from trigger selectors, those will already
172
+ // be preserved. MUST be manually prefixed with the modal selector,
173
+ // so they only apply to things inside a modal.
174
+ modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';
175
+
176
+ modal.containerSelector = '[data-blacklight-modal~=container]';
177
+
178
+ // Called on fatal failure of ajax load, function returns content
179
+ // to show to user in modal. Right now called only for network errors.
180
+ modal.onFailure = function (error) {
181
+ console.error('Server error:', this.url, error);
182
+
183
+ const contents = `<div class="modal-header">
184
+ <div class="modal-title">There was a problem with your request.</div>
185
+ <button type="button" class="blacklight-modal-close btn-close close" data-bl-dismiss="modal" aria-label="Close">
186
+ <span aria-hidden="true" class="visually-hidden">&times;</span>
187
+ </button>
188
+ </div>
189
+ <div class="modal-body">
190
+ <p>Expected a successful response from the server, but got an error</p>
191
+ <pre>${this.url}\n${error}</pre>
192
+ </div>`;
193
+
194
+ document.querySelector(`${modal.modalSelector} .modal-content`).innerHTML = contents;
195
+
196
+ modal.show();
197
+ };
198
+
199
+ // Add the passed in contents to the modal and display it.
200
+ modal.receiveAjax = function (contents) {
201
+ const domparser = new DOMParser();
202
+ const dom = domparser.parseFromString(contents, "text/html");
203
+ // If there is a containerSelector on the document, use its children.
204
+ let elements = dom.querySelectorAll(`${modal.containerSelector} > *`);
205
+ if (elements.length == 0) {
206
+ // If the containerSelector wasn't found, use the whole document
207
+ elements = dom.body.childNodes;
208
+ }
209
+
210
+ document.querySelector(`${modal.modalSelector} .modal-content`).replaceChildren(...elements);
211
+
212
+ modal.show();
213
+ };
214
+
215
+
216
+ modal.modalAjaxLinkClick = function(e) {
217
+ e.preventDefault();
218
+ const href = e.target.getAttribute('href');
219
+ fetch(href)
220
+ .then(response => {
221
+ if (!response.ok) {
222
+ throw new TypeError("Request failed");
223
+ }
224
+ return response.text();
225
+ })
226
+ .then(data => modal.receiveAjax(data))
227
+ .catch(error => modal.onFailure(error));
228
+ };
229
+
230
+ modal.setupModal = function() {
231
+ // Register both trigger and preserve selectors in ONE event handler, combining
232
+ // into one selector with a comma, so if something matches BOTH selectors, it
233
+ // still only gets the event handler called once.
234
+ document.addEventListener('click', (e) => {
235
+ if (e.target.closest(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))
236
+ modal.modalAjaxLinkClick(e);
237
+ else if (e.target.closest('[data-bl-dismiss="modal"]'))
238
+ modal.hide();
239
+ });
240
+ };
241
+
242
+ modal.hide = function (el) {
243
+ const dom = document.querySelector(modal.modalSelector);
244
+
245
+ if (!dom.open) return
246
+ dom.close();
247
+ };
248
+
249
+ modal.show = function(el) {
250
+ const dom = document.querySelector(modal.modalSelector);
251
+
252
+ if (dom.open) return
253
+ dom.showModal();
254
+ };
255
+
256
+ modal.setupModal();
257
+
258
+ return modal;
259
+ })();
260
+
261
+ const SearchContext = (e) => {
262
+ if (e.target.matches('[data-context-href]')) {
263
+ SearchContext.handleSearchContextMethod.call(e.target, e);
264
+ }
265
+ };
266
+
267
+ SearchContext.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content;
268
+ SearchContext.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content;
269
+
270
+ // this is the Rails.handleMethod with a couple adjustments, described inline:
271
+ // first, we're attaching this directly to the event handler, so we can check for meta-keys
272
+ SearchContext.handleSearchContextMethod = function(event) {
273
+ const link = this;
274
+
275
+ // instead of using the normal href, we need to use the context href instead
276
+ let href = link.getAttribute('data-context-href');
277
+ let target = link.getAttribute('target');
278
+ let csrfToken = SearchContext.csrfToken();
279
+ let csrfParam = SearchContext.csrfParam();
280
+ let form = document.createElement('form');
281
+ form.method = 'post';
282
+ form.action = href;
283
+
284
+
285
+ let formContent = `<input name="_method" value="post" type="hidden" />
286
+ <input name="redirect" value="${link.getAttribute('href')}" type="hidden" />`;
287
+
288
+ // check for meta keys.. if set, we should open in a new tab
289
+ if(event.metaKey || event.ctrlKey) {
290
+ target = '_blank';
291
+ }
292
+
293
+ if (csrfParam !== undefined && csrfToken !== undefined) {
294
+ formContent += `<input name="${csrfParam}" value="${csrfToken}" type="hidden" />`;
295
+ }
296
+
297
+ // Must trigger submit by click on a button, else "submit" event handler won't work!
298
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
299
+ formContent += '<input type="submit" />';
300
+
301
+ if (target) { form.setAttribute('target', target); }
302
+
303
+ form.style.display = 'none';
304
+ form.innerHTML = formContent;
305
+ document.body.appendChild(form);
306
+ form.querySelector('[type="submit"]').click();
307
+
308
+ event.preventDefault();
309
+ };
310
+
311
+ document.addEventListener('click', SearchContext);
312
+
313
+ const Blacklight = function() {
314
+ const buffer = new Array;
315
+ return {
316
+ onLoad: function(func) {
317
+ buffer.push(func);
318
+ },
319
+
320
+ activate: function() {
321
+ for(let i = 0; i < buffer.length; i++) {
322
+ buffer[i].call();
323
+ }
324
+ },
325
+
326
+ listeners: function () {
327
+ const listeners = [];
328
+ if (typeof Turbo !== 'undefined') {
329
+ listeners.push('turbo:load', 'turbo:frame-load');
330
+ } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
331
+ // Turbolinks 5
332
+ if (Turbolinks.BrowserAdapter) {
333
+ listeners.push('turbolinks:load');
334
+ } else {
335
+ // Turbolinks < 5
336
+ listeners.push('page:load', 'DOMContentLoaded');
337
+ }
338
+ } else {
339
+ listeners.push('DOMContentLoaded');
340
+ }
341
+
342
+ return listeners;
343
+ }
344
+ };
345
+ }();
346
+
347
+ // turbolinks triggers page:load events on page transition
348
+ // If app isn't using turbolinks, this event will never be triggered, no prob.
349
+ Blacklight.listeners().forEach(function(listener) {
350
+ document.addEventListener(listener, function() {
351
+ Blacklight.activate();
352
+ });
353
+ });
354
+
355
+ Blacklight.onLoad(function () {
356
+ const elem = document.querySelector('.no-js');
357
+
358
+ // The "no-js" class may already have been removed because this function is
359
+ // run on every turbo:load event, in that case, it won't find an element.
360
+ if (!elem) return;
361
+
362
+ elem.classList.remove('no-js');
363
+ elem.classList.add('js');
364
+ });
365
+
366
+ const index = {
367
+ BookmarkToggle,
368
+ ButtonFocus,
369
+ Modal,
370
+ SearchContext,
371
+ Core: Blacklight,
372
+ onLoad: Blacklight.onLoad
373
+ };
374
+
375
+ export { index as default };
376
+ //# sourceMappingURL=blacklight.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blacklight.esm.js","sources":["../../../javascript/blacklight/checkbox_submit.js","../../../javascript/blacklight/bookmark_toggle.js","../../../javascript/blacklight/button_focus.js","../../../javascript/blacklight/modal.js","../../../javascript/blacklight/search_context.js","../../../javascript/blacklight/core.js","../../../javascript/blacklight/index.js"],"sourcesContent":["/* Converts a \"toggle\" form, with single submit button to add/remove\n something, like used for Bookmarks, into an AJAXy checkbox instead.\n Apply to a form. Does require certain assumption about the form:\n 1) The same form 'action' href must be used for both ADD and REMOVE\n actions, with the different being the hidden input name=\"_method\"\n being set to \"put\" or \"delete\" -- that's the Rails method to pretend\n to be doing a certain HTTP verb. So same URL, PUT to add, DELETE\n to remove. This plugin assumes that.\n Plus, the form this is applied to should provide a data-doc-id\n attribute (HTML5-style doc-*) that contains the id/primary key\n of the object in question -- used by plugin for a unique value for\n DOM id's.\n Uses HTML for a checkbox compatible with Bootstrap 4.\n new CheckboxSubmit(document.querySelector('form.something')).render()\n*/\nexport default class CheckboxSubmit {\n constructor(form) {\n this.form = form\n }\n\n async clicked(evt) {\n this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress')\n this.labelTarget.setAttribute('disabled', 'disabled');\n this.checkboxTarget.setAttribute('disabled', 'disabled');\n const response = await fetch(this.formTarget.getAttribute('action'), {\n body: new FormData(this.formTarget),\n method: this.formTarget.getAttribute('method').toUpperCase(),\n headers: {\n 'Accept': 'application/json',\n 'X-Requested-With': 'XMLHttpRequest',\n 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content\n }\n })\n this.labelTarget.removeAttribute('disabled')\n this.checkboxTarget.removeAttribute('disabled')\n if (response.ok) {\n const json = await response.json()\n this.updateStateFor(!this.checked)\n document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count\n } else {\n alert('Error')\n }\n }\n\n get checked() {\n return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)\n }\n\n get formTarget() {\n return this.form\n }\n\n get labelTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"label\"]')\n }\n\n get checkboxTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"checkbox\"]')\n }\n\n get spanTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"span\"]')\n }\n\n updateStateFor(state) {\n this.checkboxTarget.checked = state\n\n if (state) {\n this.labelTarget.classList.add('checked')\n //Set the Rails hidden field that fakes an HTTP verb\n //properly for current state action.\n this.formTarget.querySelector('input[name=_method]').value = 'delete'\n this.spanTarget.innerHTML = this.form.getAttribute('data-present')\n } else {\n this.labelTarget.classList.remove('checked')\n this.formTarget.querySelector('input[name=_method]').value = 'put'\n this.spanTarget.innerHTML = this.form.getAttribute('data-absent')\n }\n }\n}\n","import CheckboxSubmit from 'blacklight/checkbox_submit'\n\nconst BookmarkToggle = (e) => {\n if (e.target.matches('[data-checkboxsubmit-target=\"checkbox\"]')) {\n const form = e.target.closest('form')\n if (form) new CheckboxSubmit(form).clicked(e);\n }\n};\n\ndocument.addEventListener('click', BookmarkToggle);\n\nexport default BookmarkToggle\n","const ButtonFocus = (e) => {\n // Button clicks should change focus. As of 10/3/19, Firefox for Mac and\n // Safari both do not set focus to a button on button click.\n // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information\n if (e.target.matches('[data-toggle=\"collapse\"]') || e.target.matches('[data-bs-toggle=\"collapse\"]')) {\n e.target.focus()\n }\n}\n\ndocument.addEventListener('click', ButtonFocus)\n\nexport default ButtonFocus\n","/*\n The blacklight modal plugin can display some interactions inside a Bootstrap\n modal window, including some multi-page interactions.\n\n It supports unobtrusive Javascript, where a link or form that would have caused\n a new page load is changed to display it's results inside a modal dialog,\n by this plugin. The plugin assumes there is a Bootstrap modal div\n on the page with id #blacklight-modal to use as the modal -- the standard Blacklight\n layout provides this.\n\n To make a link or form have their results display inside a modal, add\n `data-blacklight-modal=\"trigger\"` to the link or form. (Note, form itself not submit input)\n With Rails link_to helper, you'd do that like:\n\n link_to something, link, data: { blacklight_modal: \"trigger\" }\n\n The results of the link href or form submit will be displayed inside\n a modal -- they should include the proper HTML markup for a bootstrap modal's\n contents. Also, you ordinarily won't want the Rails template with wrapping\n navigational elements to be used. The Rails controller could suppress\n the layout when a JS AJAX request is detected, OR the response\n can include a `<div data-blacklight-modal=\"container\">` -- only the contents\n of the container will be placed inside the modal, the rest of the\n page will be ignored.\n\n Link or forms inside the modal will ordinarily cause page loads\n when they are triggered. However, if you'd like their results\n to stay within the modal, just add `data-blacklight-modal=\"preserve\"`\n to the link or form.\n\n Here's an example of what might be returned, demonstrating most of the devices available:\n\n <div data-blacklight-modal=\"container\">\n <div class=\"modal-header\">\n <button type=\"button\" class=\"close\" data-bl-dismiss=\"modal\" aria-hidden=\"true\">×</button>\n <h3 class=\"modal-title\">Request Placed</h3>\n </div>\n\n <div class=\"modal-body\">\n <p>Some message</p>\n <%= link_to \"This result will still be within modal\", some_link, data: { blacklight_modal: \"preserve\" } %>\n </div>\n\n\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" data-bl-dismiss=\"modal\">Close</button>\n </div>\n </div>\n\n\n One additional feature. If the content returned from the AJAX form submission\n can be a turbo-stream that defines some HTML fragementsand where on the page to put them:\n https://turbo.hotwired.dev/handbook/streams\n*/\nimport ModalForm from 'blacklight/modalForm'\n\nconst Modal = (() => {\n const modal = {}\n\n // a Bootstrap modal div that should be already on the page hidden\n modal.modalSelector = '#blacklight-modal';\n\n // Trigger selectors identify forms or hyperlinks that should open\n // inside a modal dialog.\n modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';\n\n // preserve selectors identify forms or hyperlinks that, if activated already\n // inside a modal dialog, should have destinations remain inside the modal -- but\n // won't trigger a modal if not already in one.\n //\n // No need to repeat selectors from trigger selectors, those will already\n // be preserved. MUST be manually prefixed with the modal selector,\n // so they only apply to things inside a modal.\n modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';\n\n modal.containerSelector = '[data-blacklight-modal~=container]';\n\n // Called on fatal failure of ajax load, function returns content\n // to show to user in modal. Right now called only for network errors.\n modal.onFailure = function (error) {\n console.error('Server error:', this.url, error);\n\n const contents = `<div class=\"modal-header\">\n <div class=\"modal-title\">There was a problem with your request.</div>\n <button type=\"button\" class=\"blacklight-modal-close btn-close close\" data-bl-dismiss=\"modal\" aria-label=\"Close\">\n <span aria-hidden=\"true\" class=\"visually-hidden\">&times;</span>\n </button>\n </div>\n <div class=\"modal-body\">\n <p>Expected a successful response from the server, but got an error</p>\n <pre>${this.url}\\n${error}</pre>\n </div>`\n\n document.querySelector(`${modal.modalSelector} .modal-content`).innerHTML = contents\n\n modal.show();\n }\n\n // Add the passed in contents to the modal and display it.\n modal.receiveAjax = function (contents) {\n const domparser = new DOMParser();\n const dom = domparser.parseFromString(contents, \"text/html\")\n // If there is a containerSelector on the document, use its children.\n let elements = dom.querySelectorAll(`${modal.containerSelector} > *`)\n if (elements.length == 0) {\n // If the containerSelector wasn't found, use the whole document\n elements = dom.body.childNodes\n }\n\n document.querySelector(`${modal.modalSelector} .modal-content`).replaceChildren(...elements)\n\n modal.show();\n };\n\n\n modal.modalAjaxLinkClick = function(e) {\n e.preventDefault();\n const href = e.target.getAttribute('href')\n fetch(href)\n .then(response => {\n if (!response.ok) {\n throw new TypeError(\"Request failed\");\n }\n return response.text();\n })\n .then(data => modal.receiveAjax(data))\n .catch(error => modal.onFailure(error))\n };\n\n modal.setupModal = function() {\n // Register both trigger and preserve selectors in ONE event handler, combining\n // into one selector with a comma, so if something matches BOTH selectors, it\n // still only gets the event handler called once.\n document.addEventListener('click', (e) => {\n if (e.target.closest(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))\n modal.modalAjaxLinkClick(e)\n else if (e.target.closest('[data-bl-dismiss=\"modal\"]'))\n modal.hide()\n })\n };\n\n modal.hide = function (el) {\n const dom = document.querySelector(modal.modalSelector)\n\n if (!dom.open) return\n dom.close()\n }\n\n modal.show = function(el) {\n const dom = document.querySelector(modal.modalSelector)\n\n if (dom.open) return\n dom.showModal()\n }\n\n modal.setupModal()\n\n return modal;\n})()\n\nexport default Modal\n","const SearchContext = (e) => {\n if (e.target.matches('[data-context-href]')) {\n SearchContext.handleSearchContextMethod.call(e.target, e)\n }\n}\n\nSearchContext.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content\nSearchContext.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content\n\n// this is the Rails.handleMethod with a couple adjustments, described inline:\n// first, we're attaching this directly to the event handler, so we can check for meta-keys\nSearchContext.handleSearchContextMethod = function(event) {\n const link = this\n\n // instead of using the normal href, we need to use the context href instead\n let href = link.getAttribute('data-context-href')\n let target = link.getAttribute('target')\n let csrfToken = SearchContext.csrfToken()\n let csrfParam = SearchContext.csrfParam()\n let form = document.createElement('form')\n form.method = 'post'\n form.action = href\n\n\n let formContent = `<input name=\"_method\" value=\"post\" type=\"hidden\" />\n <input name=\"redirect\" value=\"${link.getAttribute('href')}\" type=\"hidden\" />`\n\n // check for meta keys.. if set, we should open in a new tab\n if(event.metaKey || event.ctrlKey) {\n target = '_blank';\n }\n\n if (csrfParam !== undefined && csrfToken !== undefined) {\n formContent += `<input name=\"${csrfParam}\" value=\"${csrfToken}\" type=\"hidden\" />`\n }\n\n // Must trigger submit by click on a button, else \"submit\" event handler won't work!\n // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit\n formContent += '<input type=\"submit\" />'\n\n if (target) { form.setAttribute('target', target); }\n\n form.style.display = 'none'\n form.innerHTML = formContent\n document.body.appendChild(form)\n form.querySelector('[type=\"submit\"]').click()\n\n event.preventDefault()\n};\n\ndocument.addEventListener('click', SearchContext)\n\nexport default SearchContext\n","const Blacklight = function() {\n const buffer = new Array;\n return {\n onLoad: function(func) {\n buffer.push(func);\n },\n\n activate: function() {\n for(let i = 0; i < buffer.length; i++) {\n buffer[i].call();\n }\n },\n\n listeners: function () {\n const listeners = [];\n if (typeof Turbo !== 'undefined') {\n listeners.push('turbo:load', 'turbo:frame-load');\n } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {\n // Turbolinks 5\n if (Turbolinks.BrowserAdapter) {\n listeners.push('turbolinks:load');\n } else {\n // Turbolinks < 5\n listeners.push('page:load', 'DOMContentLoaded');\n }\n } else {\n listeners.push('DOMContentLoaded');\n }\n\n return listeners;\n }\n };\n}();\n\n// turbolinks triggers page:load events on page transition\n// If app isn't using turbolinks, this event will never be triggered, no prob.\nBlacklight.listeners().forEach(function(listener) {\n document.addEventListener(listener, function() {\n Blacklight.activate()\n })\n})\n\nBlacklight.onLoad(function () {\n const elem = document.querySelector('.no-js');\n\n // The \"no-js\" class may already have been removed because this function is\n // run on every turbo:load event, in that case, it won't find an element.\n if (!elem) return;\n\n elem.classList.remove('no-js')\n elem.classList.add('js')\n})\n\n\nexport default Blacklight\n","import BookmarkToggle from 'blacklight/bookmark_toggle'\nimport ButtonFocus from 'blacklight/button_focus'\nimport Modal from 'blacklight/modal'\nimport SearchContext from 'blacklight/search_context'\nimport Core from 'blacklight/core'\n\nexport default {\n BookmarkToggle,\n ButtonFocus,\n Modal,\n SearchContext,\n Core,\n onLoad: Core.onLoad\n}\n"],"names":["Core"],"mappings":"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"}
@@ -0,0 +1,384 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
+ typeof define === 'function' && define.amd ? define(factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Blacklight = factory());
5
+ })(this, (function () { 'use strict';
6
+
7
+ /* Converts a "toggle" form, with single submit button to add/remove
8
+ something, like used for Bookmarks, into an AJAXy checkbox instead.
9
+ Apply to a form. Does require certain assumption about the form:
10
+ 1) The same form 'action' href must be used for both ADD and REMOVE
11
+ actions, with the different being the hidden input name="_method"
12
+ being set to "put" or "delete" -- that's the Rails method to pretend
13
+ to be doing a certain HTTP verb. So same URL, PUT to add, DELETE
14
+ to remove. This plugin assumes that.
15
+ Plus, the form this is applied to should provide a data-doc-id
16
+ attribute (HTML5-style doc-*) that contains the id/primary key
17
+ of the object in question -- used by plugin for a unique value for
18
+ DOM id's.
19
+ Uses HTML for a checkbox compatible with Bootstrap 4.
20
+ new CheckboxSubmit(document.querySelector('form.something')).render()
21
+ */
22
+ class CheckboxSubmit {
23
+ constructor(form) {
24
+ this.form = form;
25
+ }
26
+
27
+ async clicked(evt) {
28
+ this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress');
29
+ this.labelTarget.setAttribute('disabled', 'disabled');
30
+ this.checkboxTarget.setAttribute('disabled', 'disabled');
31
+ const response = await fetch(this.formTarget.getAttribute('action'), {
32
+ body: new FormData(this.formTarget),
33
+ method: this.formTarget.getAttribute('method').toUpperCase(),
34
+ headers: {
35
+ 'Accept': 'application/json',
36
+ 'X-Requested-With': 'XMLHttpRequest',
37
+ 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content
38
+ }
39
+ });
40
+ this.labelTarget.removeAttribute('disabled');
41
+ this.checkboxTarget.removeAttribute('disabled');
42
+ if (response.ok) {
43
+ const json = await response.json();
44
+ this.updateStateFor(!this.checked);
45
+ document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count;
46
+ } else {
47
+ alert('Error');
48
+ }
49
+ }
50
+
51
+ get checked() {
52
+ return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)
53
+ }
54
+
55
+ get formTarget() {
56
+ return this.form
57
+ }
58
+
59
+ get labelTarget() {
60
+ return this.form.querySelector('[data-checkboxsubmit-target="label"]')
61
+ }
62
+
63
+ get checkboxTarget() {
64
+ return this.form.querySelector('[data-checkboxsubmit-target="checkbox"]')
65
+ }
66
+
67
+ get spanTarget() {
68
+ return this.form.querySelector('[data-checkboxsubmit-target="span"]')
69
+ }
70
+
71
+ updateStateFor(state) {
72
+ this.checkboxTarget.checked = state;
73
+
74
+ if (state) {
75
+ this.labelTarget.classList.add('checked');
76
+ //Set the Rails hidden field that fakes an HTTP verb
77
+ //properly for current state action.
78
+ this.formTarget.querySelector('input[name=_method]').value = 'delete';
79
+ this.spanTarget.innerHTML = this.form.getAttribute('data-present');
80
+ } else {
81
+ this.labelTarget.classList.remove('checked');
82
+ this.formTarget.querySelector('input[name=_method]').value = 'put';
83
+ this.spanTarget.innerHTML = this.form.getAttribute('data-absent');
84
+ }
85
+ }
86
+ }
87
+
88
+ const BookmarkToggle = (e) => {
89
+ if (e.target.matches('[data-checkboxsubmit-target="checkbox"]')) {
90
+ const form = e.target.closest('form');
91
+ if (form) new CheckboxSubmit(form).clicked(e);
92
+ }
93
+ };
94
+
95
+ document.addEventListener('click', BookmarkToggle);
96
+
97
+ const ButtonFocus = (e) => {
98
+ // Button clicks should change focus. As of 10/3/19, Firefox for Mac and
99
+ // Safari both do not set focus to a button on button click.
100
+ // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information
101
+ if (e.target.matches('[data-toggle="collapse"]') || e.target.matches('[data-bs-toggle="collapse"]')) {
102
+ e.target.focus();
103
+ }
104
+ };
105
+
106
+ document.addEventListener('click', ButtonFocus);
107
+
108
+ /*
109
+ The blacklight modal plugin can display some interactions inside a Bootstrap
110
+ modal window, including some multi-page interactions.
111
+
112
+ It supports unobtrusive Javascript, where a link or form that would have caused
113
+ a new page load is changed to display it's results inside a modal dialog,
114
+ by this plugin. The plugin assumes there is a Bootstrap modal div
115
+ on the page with id #blacklight-modal to use as the modal -- the standard Blacklight
116
+ layout provides this.
117
+
118
+ To make a link or form have their results display inside a modal, add
119
+ `data-blacklight-modal="trigger"` to the link or form. (Note, form itself not submit input)
120
+ With Rails link_to helper, you'd do that like:
121
+
122
+ link_to something, link, data: { blacklight_modal: "trigger" }
123
+
124
+ The results of the link href or form submit will be displayed inside
125
+ a modal -- they should include the proper HTML markup for a bootstrap modal's
126
+ contents. Also, you ordinarily won't want the Rails template with wrapping
127
+ navigational elements to be used. The Rails controller could suppress
128
+ the layout when a JS AJAX request is detected, OR the response
129
+ can include a `<div data-blacklight-modal="container">` -- only the contents
130
+ of the container will be placed inside the modal, the rest of the
131
+ page will be ignored.
132
+
133
+ Link or forms inside the modal will ordinarily cause page loads
134
+ when they are triggered. However, if you'd like their results
135
+ to stay within the modal, just add `data-blacklight-modal="preserve"`
136
+ to the link or form.
137
+
138
+ Here's an example of what might be returned, demonstrating most of the devices available:
139
+
140
+ <div data-blacklight-modal="container">
141
+ <div class="modal-header">
142
+ <button type="button" class="close" data-bl-dismiss="modal" aria-hidden="true">×</button>
143
+ <h3 class="modal-title">Request Placed</h3>
144
+ </div>
145
+
146
+ <div class="modal-body">
147
+ <p>Some message</p>
148
+ <%= link_to "This result will still be within modal", some_link, data: { blacklight_modal: "preserve" } %>
149
+ </div>
150
+
151
+
152
+ <div class="modal-footer">
153
+ <button type="button" class="btn btn-secondary" data-bl-dismiss="modal">Close</button>
154
+ </div>
155
+ </div>
156
+
157
+
158
+ One additional feature. If the content returned from the AJAX form submission
159
+ can be a turbo-stream that defines some HTML fragementsand where on the page to put them:
160
+ https://turbo.hotwired.dev/handbook/streams
161
+ */
162
+
163
+ const Modal = (() => {
164
+ const modal = {};
165
+
166
+ // a Bootstrap modal div that should be already on the page hidden
167
+ modal.modalSelector = '#blacklight-modal';
168
+
169
+ // Trigger selectors identify forms or hyperlinks that should open
170
+ // inside a modal dialog.
171
+ modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';
172
+
173
+ // preserve selectors identify forms or hyperlinks that, if activated already
174
+ // inside a modal dialog, should have destinations remain inside the modal -- but
175
+ // won't trigger a modal if not already in one.
176
+ //
177
+ // No need to repeat selectors from trigger selectors, those will already
178
+ // be preserved. MUST be manually prefixed with the modal selector,
179
+ // so they only apply to things inside a modal.
180
+ modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';
181
+
182
+ modal.containerSelector = '[data-blacklight-modal~=container]';
183
+
184
+ // Called on fatal failure of ajax load, function returns content
185
+ // to show to user in modal. Right now called only for network errors.
186
+ modal.onFailure = function (error) {
187
+ console.error('Server error:', this.url, error);
188
+
189
+ const contents = `<div class="modal-header">
190
+ <div class="modal-title">There was a problem with your request.</div>
191
+ <button type="button" class="blacklight-modal-close btn-close close" data-bl-dismiss="modal" aria-label="Close">
192
+ <span aria-hidden="true" class="visually-hidden">&times;</span>
193
+ </button>
194
+ </div>
195
+ <div class="modal-body">
196
+ <p>Expected a successful response from the server, but got an error</p>
197
+ <pre>${this.url}\n${error}</pre>
198
+ </div>`;
199
+
200
+ document.querySelector(`${modal.modalSelector} .modal-content`).innerHTML = contents;
201
+
202
+ modal.show();
203
+ };
204
+
205
+ // Add the passed in contents to the modal and display it.
206
+ modal.receiveAjax = function (contents) {
207
+ const domparser = new DOMParser();
208
+ const dom = domparser.parseFromString(contents, "text/html");
209
+ // If there is a containerSelector on the document, use its children.
210
+ let elements = dom.querySelectorAll(`${modal.containerSelector} > *`);
211
+ if (elements.length == 0) {
212
+ // If the containerSelector wasn't found, use the whole document
213
+ elements = dom.body.childNodes;
214
+ }
215
+
216
+ document.querySelector(`${modal.modalSelector} .modal-content`).replaceChildren(...elements);
217
+
218
+ modal.show();
219
+ };
220
+
221
+
222
+ modal.modalAjaxLinkClick = function(e) {
223
+ e.preventDefault();
224
+ const href = e.target.getAttribute('href');
225
+ fetch(href)
226
+ .then(response => {
227
+ if (!response.ok) {
228
+ throw new TypeError("Request failed");
229
+ }
230
+ return response.text();
231
+ })
232
+ .then(data => modal.receiveAjax(data))
233
+ .catch(error => modal.onFailure(error));
234
+ };
235
+
236
+ modal.setupModal = function() {
237
+ // Register both trigger and preserve selectors in ONE event handler, combining
238
+ // into one selector with a comma, so if something matches BOTH selectors, it
239
+ // still only gets the event handler called once.
240
+ document.addEventListener('click', (e) => {
241
+ if (e.target.closest(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))
242
+ modal.modalAjaxLinkClick(e);
243
+ else if (e.target.closest('[data-bl-dismiss="modal"]'))
244
+ modal.hide();
245
+ });
246
+ };
247
+
248
+ modal.hide = function (el) {
249
+ const dom = document.querySelector(modal.modalSelector);
250
+
251
+ if (!dom.open) return
252
+ dom.close();
253
+ };
254
+
255
+ modal.show = function(el) {
256
+ const dom = document.querySelector(modal.modalSelector);
257
+
258
+ if (dom.open) return
259
+ dom.showModal();
260
+ };
261
+
262
+ modal.setupModal();
263
+
264
+ return modal;
265
+ })();
266
+
267
+ const SearchContext = (e) => {
268
+ if (e.target.matches('[data-context-href]')) {
269
+ SearchContext.handleSearchContextMethod.call(e.target, e);
270
+ }
271
+ };
272
+
273
+ SearchContext.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content;
274
+ SearchContext.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content;
275
+
276
+ // this is the Rails.handleMethod with a couple adjustments, described inline:
277
+ // first, we're attaching this directly to the event handler, so we can check for meta-keys
278
+ SearchContext.handleSearchContextMethod = function(event) {
279
+ const link = this;
280
+
281
+ // instead of using the normal href, we need to use the context href instead
282
+ let href = link.getAttribute('data-context-href');
283
+ let target = link.getAttribute('target');
284
+ let csrfToken = SearchContext.csrfToken();
285
+ let csrfParam = SearchContext.csrfParam();
286
+ let form = document.createElement('form');
287
+ form.method = 'post';
288
+ form.action = href;
289
+
290
+
291
+ let formContent = `<input name="_method" value="post" type="hidden" />
292
+ <input name="redirect" value="${link.getAttribute('href')}" type="hidden" />`;
293
+
294
+ // check for meta keys.. if set, we should open in a new tab
295
+ if(event.metaKey || event.ctrlKey) {
296
+ target = '_blank';
297
+ }
298
+
299
+ if (csrfParam !== undefined && csrfToken !== undefined) {
300
+ formContent += `<input name="${csrfParam}" value="${csrfToken}" type="hidden" />`;
301
+ }
302
+
303
+ // Must trigger submit by click on a button, else "submit" event handler won't work!
304
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
305
+ formContent += '<input type="submit" />';
306
+
307
+ if (target) { form.setAttribute('target', target); }
308
+
309
+ form.style.display = 'none';
310
+ form.innerHTML = formContent;
311
+ document.body.appendChild(form);
312
+ form.querySelector('[type="submit"]').click();
313
+
314
+ event.preventDefault();
315
+ };
316
+
317
+ document.addEventListener('click', SearchContext);
318
+
319
+ const Blacklight = function() {
320
+ const buffer = new Array;
321
+ return {
322
+ onLoad: function(func) {
323
+ buffer.push(func);
324
+ },
325
+
326
+ activate: function() {
327
+ for(let i = 0; i < buffer.length; i++) {
328
+ buffer[i].call();
329
+ }
330
+ },
331
+
332
+ listeners: function () {
333
+ const listeners = [];
334
+ if (typeof Turbo !== 'undefined') {
335
+ listeners.push('turbo:load', 'turbo:frame-load');
336
+ } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
337
+ // Turbolinks 5
338
+ if (Turbolinks.BrowserAdapter) {
339
+ listeners.push('turbolinks:load');
340
+ } else {
341
+ // Turbolinks < 5
342
+ listeners.push('page:load', 'DOMContentLoaded');
343
+ }
344
+ } else {
345
+ listeners.push('DOMContentLoaded');
346
+ }
347
+
348
+ return listeners;
349
+ }
350
+ };
351
+ }();
352
+
353
+ // turbolinks triggers page:load events on page transition
354
+ // If app isn't using turbolinks, this event will never be triggered, no prob.
355
+ Blacklight.listeners().forEach(function(listener) {
356
+ document.addEventListener(listener, function() {
357
+ Blacklight.activate();
358
+ });
359
+ });
360
+
361
+ Blacklight.onLoad(function () {
362
+ const elem = document.querySelector('.no-js');
363
+
364
+ // The "no-js" class may already have been removed because this function is
365
+ // run on every turbo:load event, in that case, it won't find an element.
366
+ if (!elem) return;
367
+
368
+ elem.classList.remove('no-js');
369
+ elem.classList.add('js');
370
+ });
371
+
372
+ const index = {
373
+ BookmarkToggle,
374
+ ButtonFocus,
375
+ Modal,
376
+ SearchContext,
377
+ Core: Blacklight,
378
+ onLoad: Blacklight.onLoad
379
+ };
380
+
381
+ return index;
382
+
383
+ }));
384
+ //# sourceMappingURL=blacklight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blacklight.js","sources":["../../../javascript/blacklight/checkbox_submit.js","../../../javascript/blacklight/bookmark_toggle.js","../../../javascript/blacklight/button_focus.js","../../../javascript/blacklight/modal.js","../../../javascript/blacklight/search_context.js","../../../javascript/blacklight/core.js","../../../javascript/blacklight/index.js"],"sourcesContent":["/* Converts a \"toggle\" form, with single submit button to add/remove\n something, like used for Bookmarks, into an AJAXy checkbox instead.\n Apply to a form. Does require certain assumption about the form:\n 1) The same form 'action' href must be used for both ADD and REMOVE\n actions, with the different being the hidden input name=\"_method\"\n being set to \"put\" or \"delete\" -- that's the Rails method to pretend\n to be doing a certain HTTP verb. So same URL, PUT to add, DELETE\n to remove. This plugin assumes that.\n Plus, the form this is applied to should provide a data-doc-id\n attribute (HTML5-style doc-*) that contains the id/primary key\n of the object in question -- used by plugin for a unique value for\n DOM id's.\n Uses HTML for a checkbox compatible with Bootstrap 4.\n new CheckboxSubmit(document.querySelector('form.something')).render()\n*/\nexport default class CheckboxSubmit {\n constructor(form) {\n this.form = form\n }\n\n async clicked(evt) {\n this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress')\n this.labelTarget.setAttribute('disabled', 'disabled');\n this.checkboxTarget.setAttribute('disabled', 'disabled');\n const response = await fetch(this.formTarget.getAttribute('action'), {\n body: new FormData(this.formTarget),\n method: this.formTarget.getAttribute('method').toUpperCase(),\n headers: {\n 'Accept': 'application/json',\n 'X-Requested-With': 'XMLHttpRequest',\n 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content\n }\n })\n this.labelTarget.removeAttribute('disabled')\n this.checkboxTarget.removeAttribute('disabled')\n if (response.ok) {\n const json = await response.json()\n this.updateStateFor(!this.checked)\n document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count\n } else {\n alert('Error')\n }\n }\n\n get checked() {\n return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)\n }\n\n get formTarget() {\n return this.form\n }\n\n get labelTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"label\"]')\n }\n\n get checkboxTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"checkbox\"]')\n }\n\n get spanTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"span\"]')\n }\n\n updateStateFor(state) {\n this.checkboxTarget.checked = state\n\n if (state) {\n this.labelTarget.classList.add('checked')\n //Set the Rails hidden field that fakes an HTTP verb\n //properly for current state action.\n this.formTarget.querySelector('input[name=_method]').value = 'delete'\n this.spanTarget.innerHTML = this.form.getAttribute('data-present')\n } else {\n this.labelTarget.classList.remove('checked')\n this.formTarget.querySelector('input[name=_method]').value = 'put'\n this.spanTarget.innerHTML = this.form.getAttribute('data-absent')\n }\n }\n}\n","import CheckboxSubmit from 'blacklight/checkbox_submit'\n\nconst BookmarkToggle = (e) => {\n if (e.target.matches('[data-checkboxsubmit-target=\"checkbox\"]')) {\n const form = e.target.closest('form')\n if (form) new CheckboxSubmit(form).clicked(e);\n }\n};\n\ndocument.addEventListener('click', BookmarkToggle);\n\nexport default BookmarkToggle\n","const ButtonFocus = (e) => {\n // Button clicks should change focus. As of 10/3/19, Firefox for Mac and\n // Safari both do not set focus to a button on button click.\n // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information\n if (e.target.matches('[data-toggle=\"collapse\"]') || e.target.matches('[data-bs-toggle=\"collapse\"]')) {\n e.target.focus()\n }\n}\n\ndocument.addEventListener('click', ButtonFocus)\n\nexport default ButtonFocus\n","/*\n The blacklight modal plugin can display some interactions inside a Bootstrap\n modal window, including some multi-page interactions.\n\n It supports unobtrusive Javascript, where a link or form that would have caused\n a new page load is changed to display it's results inside a modal dialog,\n by this plugin. The plugin assumes there is a Bootstrap modal div\n on the page with id #blacklight-modal to use as the modal -- the standard Blacklight\n layout provides this.\n\n To make a link or form have their results display inside a modal, add\n `data-blacklight-modal=\"trigger\"` to the link or form. (Note, form itself not submit input)\n With Rails link_to helper, you'd do that like:\n\n link_to something, link, data: { blacklight_modal: \"trigger\" }\n\n The results of the link href or form submit will be displayed inside\n a modal -- they should include the proper HTML markup for a bootstrap modal's\n contents. Also, you ordinarily won't want the Rails template with wrapping\n navigational elements to be used. The Rails controller could suppress\n the layout when a JS AJAX request is detected, OR the response\n can include a `<div data-blacklight-modal=\"container\">` -- only the contents\n of the container will be placed inside the modal, the rest of the\n page will be ignored.\n\n Link or forms inside the modal will ordinarily cause page loads\n when they are triggered. However, if you'd like their results\n to stay within the modal, just add `data-blacklight-modal=\"preserve\"`\n to the link or form.\n\n Here's an example of what might be returned, demonstrating most of the devices available:\n\n <div data-blacklight-modal=\"container\">\n <div class=\"modal-header\">\n <button type=\"button\" class=\"close\" data-bl-dismiss=\"modal\" aria-hidden=\"true\">×</button>\n <h3 class=\"modal-title\">Request Placed</h3>\n </div>\n\n <div class=\"modal-body\">\n <p>Some message</p>\n <%= link_to \"This result will still be within modal\", some_link, data: { blacklight_modal: \"preserve\" } %>\n </div>\n\n\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" data-bl-dismiss=\"modal\">Close</button>\n </div>\n </div>\n\n\n One additional feature. If the content returned from the AJAX form submission\n can be a turbo-stream that defines some HTML fragementsand where on the page to put them:\n https://turbo.hotwired.dev/handbook/streams\n*/\nimport ModalForm from 'blacklight/modalForm'\n\nconst Modal = (() => {\n const modal = {}\n\n // a Bootstrap modal div that should be already on the page hidden\n modal.modalSelector = '#blacklight-modal';\n\n // Trigger selectors identify forms or hyperlinks that should open\n // inside a modal dialog.\n modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';\n\n // preserve selectors identify forms or hyperlinks that, if activated already\n // inside a modal dialog, should have destinations remain inside the modal -- but\n // won't trigger a modal if not already in one.\n //\n // No need to repeat selectors from trigger selectors, those will already\n // be preserved. MUST be manually prefixed with the modal selector,\n // so they only apply to things inside a modal.\n modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';\n\n modal.containerSelector = '[data-blacklight-modal~=container]';\n\n // Called on fatal failure of ajax load, function returns content\n // to show to user in modal. Right now called only for network errors.\n modal.onFailure = function (error) {\n console.error('Server error:', this.url, error);\n\n const contents = `<div class=\"modal-header\">\n <div class=\"modal-title\">There was a problem with your request.</div>\n <button type=\"button\" class=\"blacklight-modal-close btn-close close\" data-bl-dismiss=\"modal\" aria-label=\"Close\">\n <span aria-hidden=\"true\" class=\"visually-hidden\">&times;</span>\n </button>\n </div>\n <div class=\"modal-body\">\n <p>Expected a successful response from the server, but got an error</p>\n <pre>${this.url}\\n${error}</pre>\n </div>`\n\n document.querySelector(`${modal.modalSelector} .modal-content`).innerHTML = contents\n\n modal.show();\n }\n\n // Add the passed in contents to the modal and display it.\n modal.receiveAjax = function (contents) {\n const domparser = new DOMParser();\n const dom = domparser.parseFromString(contents, \"text/html\")\n // If there is a containerSelector on the document, use its children.\n let elements = dom.querySelectorAll(`${modal.containerSelector} > *`)\n if (elements.length == 0) {\n // If the containerSelector wasn't found, use the whole document\n elements = dom.body.childNodes\n }\n\n document.querySelector(`${modal.modalSelector} .modal-content`).replaceChildren(...elements)\n\n modal.show();\n };\n\n\n modal.modalAjaxLinkClick = function(e) {\n e.preventDefault();\n const href = e.target.getAttribute('href')\n fetch(href)\n .then(response => {\n if (!response.ok) {\n throw new TypeError(\"Request failed\");\n }\n return response.text();\n })\n .then(data => modal.receiveAjax(data))\n .catch(error => modal.onFailure(error))\n };\n\n modal.setupModal = function() {\n // Register both trigger and preserve selectors in ONE event handler, combining\n // into one selector with a comma, so if something matches BOTH selectors, it\n // still only gets the event handler called once.\n document.addEventListener('click', (e) => {\n if (e.target.closest(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))\n modal.modalAjaxLinkClick(e)\n else if (e.target.closest('[data-bl-dismiss=\"modal\"]'))\n modal.hide()\n })\n };\n\n modal.hide = function (el) {\n const dom = document.querySelector(modal.modalSelector)\n\n if (!dom.open) return\n dom.close()\n }\n\n modal.show = function(el) {\n const dom = document.querySelector(modal.modalSelector)\n\n if (dom.open) return\n dom.showModal()\n }\n\n modal.setupModal()\n\n return modal;\n})()\n\nexport default Modal\n","const SearchContext = (e) => {\n if (e.target.matches('[data-context-href]')) {\n SearchContext.handleSearchContextMethod.call(e.target, e)\n }\n}\n\nSearchContext.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content\nSearchContext.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content\n\n// this is the Rails.handleMethod with a couple adjustments, described inline:\n// first, we're attaching this directly to the event handler, so we can check for meta-keys\nSearchContext.handleSearchContextMethod = function(event) {\n const link = this\n\n // instead of using the normal href, we need to use the context href instead\n let href = link.getAttribute('data-context-href')\n let target = link.getAttribute('target')\n let csrfToken = SearchContext.csrfToken()\n let csrfParam = SearchContext.csrfParam()\n let form = document.createElement('form')\n form.method = 'post'\n form.action = href\n\n\n let formContent = `<input name=\"_method\" value=\"post\" type=\"hidden\" />\n <input name=\"redirect\" value=\"${link.getAttribute('href')}\" type=\"hidden\" />`\n\n // check for meta keys.. if set, we should open in a new tab\n if(event.metaKey || event.ctrlKey) {\n target = '_blank';\n }\n\n if (csrfParam !== undefined && csrfToken !== undefined) {\n formContent += `<input name=\"${csrfParam}\" value=\"${csrfToken}\" type=\"hidden\" />`\n }\n\n // Must trigger submit by click on a button, else \"submit\" event handler won't work!\n // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit\n formContent += '<input type=\"submit\" />'\n\n if (target) { form.setAttribute('target', target); }\n\n form.style.display = 'none'\n form.innerHTML = formContent\n document.body.appendChild(form)\n form.querySelector('[type=\"submit\"]').click()\n\n event.preventDefault()\n};\n\ndocument.addEventListener('click', SearchContext)\n\nexport default SearchContext\n","const Blacklight = function() {\n const buffer = new Array;\n return {\n onLoad: function(func) {\n buffer.push(func);\n },\n\n activate: function() {\n for(let i = 0; i < buffer.length; i++) {\n buffer[i].call();\n }\n },\n\n listeners: function () {\n const listeners = [];\n if (typeof Turbo !== 'undefined') {\n listeners.push('turbo:load', 'turbo:frame-load');\n } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {\n // Turbolinks 5\n if (Turbolinks.BrowserAdapter) {\n listeners.push('turbolinks:load');\n } else {\n // Turbolinks < 5\n listeners.push('page:load', 'DOMContentLoaded');\n }\n } else {\n listeners.push('DOMContentLoaded');\n }\n\n return listeners;\n }\n };\n}();\n\n// turbolinks triggers page:load events on page transition\n// If app isn't using turbolinks, this event will never be triggered, no prob.\nBlacklight.listeners().forEach(function(listener) {\n document.addEventListener(listener, function() {\n Blacklight.activate()\n })\n})\n\nBlacklight.onLoad(function () {\n const elem = document.querySelector('.no-js');\n\n // The \"no-js\" class may already have been removed because this function is\n // run on every turbo:load event, in that case, it won't find an element.\n if (!elem) return;\n\n elem.classList.remove('no-js')\n elem.classList.add('js')\n})\n\n\nexport default Blacklight\n","import BookmarkToggle from 'blacklight/bookmark_toggle'\nimport ButtonFocus from 'blacklight/button_focus'\nimport Modal from 'blacklight/modal'\nimport SearchContext from 'blacklight/search_context'\nimport Core from 'blacklight/core'\n\nexport default {\n BookmarkToggle,\n ButtonFocus,\n Modal,\n SearchContext,\n Core,\n onLoad: Core.onLoad\n}\n"],"names":["Core"],"mappings":"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"}
@@ -6,15 +6,17 @@ module Blacklight
6
6
  ##
7
7
  # A component for rendering a single document
8
8
  #
9
- # @note when subclassing this component, you must explicitly specify the collection parameter
10
- # as `document` and handle the `document` parameter in your initializer.
9
+ # @note when subclassing this component, if you override the initializer,
10
+ # you must explicitly specify the counter variable `document_counter` even if you don't use it.
11
+ # Otherwise view_component will not provide the count value when calling the component.
12
+ #
13
+ # @see https://viewcomponent.org/guide/collections.html#collection-counter
11
14
  #
12
15
  # @example
13
16
  # class MyDocumentComponent < Blacklight::DocumentComponent
14
- # with_collection_parameter :document
15
- #
16
- # def initialize(document:, **kwargs)
17
- # super(document: document, **kwargs)
17
+ # def initialize(document_counter: nil, **kwargs)
18
+ # super
19
+ # ... custom code ...
18
20
  # end
19
21
  # end
20
22
  class DocumentComponent < Blacklight::Component
@@ -54,7 +56,7 @@ module Blacklight
54
56
  renders_one :metadata, (lambda do |static_content = nil, *args, component: nil, fields: nil, **kwargs|
55
57
  next static_content if static_content.present?
56
58
 
57
- component ||= Blacklight::DocumentMetadataComponent
59
+ component ||= @presenter&.view_config&.metadata_component || Blacklight::DocumentMetadataComponent
58
60
 
59
61
  component.new(*args, fields: fields || @presenter&.field_presenters || [], **kwargs)
60
62
  end)
data/blacklight.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
17
17
  full text and/or metadata.)
18
18
  s.license = "Apache 2.0"
19
19
 
20
- s.files = `git ls-files -z`.split("\x0") + Dir.glob("app/assets/javascript/blacklight/**/*")
20
+ s.files = `git ls-files -z`.split("\x0") + Dir.glob("app/assets/javascripts/blacklight/**/*")
21
21
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
22
22
  s.bindir = 'exe'
23
23
  s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'shellwords'
4
+
3
5
  module Blacklight
4
6
  class AssetsGenerator < Rails::Generators::Base
5
7
  class_option :'bootstrap-version', type: :string, default: ENV.fetch('BOOTSTRAP_VERSION', '~> 5.1'), desc: "Set the generated app's bootstrap version"
6
8
 
7
9
  def run_asset_pipeline_specific_generator
8
- generated_options = "--bootstrap-version='#{options[:'bootstrap-version']}'" if options[:'bootstrap-version']
10
+ generated_options = "--bootstrap-version='#{Shellwords.escape(options[:'bootstrap-version'])}'" if options[:'bootstrap-version']
9
11
 
10
12
  generator = if defined?(Propshaft)
11
13
  'blacklight:assets:propshaft'
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'shellwords'
4
+
3
5
  module Blacklight
4
6
  class Install < Rails::Generators::Base
5
7
  source_root File.expand_path('../templates', __FILE__)
@@ -35,7 +37,7 @@ module Blacklight
35
37
  # Call external generator in AssetsGenerator, so we can
36
38
  # leave that callable seperately too.
37
39
  def copy_public_assets
38
- generated_options = "--bootstrap-version='#{options[:'bootstrap-version']}'" if options[:'bootstrap-version']
40
+ generated_options = "--bootstrap-version='#{Shellwords.escape(options[:'bootstrap-version'])}'" if options[:'bootstrap-version']
39
41
 
40
42
  generate "blacklight:assets", generated_options unless options[:'skip-assets']
41
43
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blacklight-frontend",
3
- "version": "8.0.0-beta.6",
3
+ "version": "8.0.0-beta.8",
4
4
  "description": "[![Build Status](https://travis-ci.com/projectblacklight/blacklight.png?branch=main)](https://travis-ci.com/projectblacklight/blacklight) [![Gem Version](https://badge.fury.io/rb/blacklight.png)](http://badge.fury.io/rb/blacklight) [![Coverage Status](https://coveralls.io/repos/github/projectblacklight/blacklight/badge.svg?branch=main)](https://coveralls.io/github/projectblacklight/blacklight?branch=main)",
5
5
  "main": "app/assets/javascripts/blacklight",
6
6
  "module": "app/assets/javascripts/blacklight/blacklight.esm.js",
@@ -76,6 +76,10 @@ RSpec.describe Blacklight::DocumentComponent, type: :component do
76
76
  end
77
77
 
78
78
  context 'index view' do
79
+ before do
80
+ controller.action_name = "index"
81
+ end
82
+
79
83
  let(:attr) { { counter: 5 } }
80
84
 
81
85
  it 'has data properties' do
@@ -111,13 +115,22 @@ RSpec.describe Blacklight::DocumentComponent, type: :component do
111
115
  it 'renders a thumbnail' do
112
116
  expect(rendered).to have_selector 'a[href="/catalog/x"] img[src="http://example.com/image.jpg"]'
113
117
  end
118
+
119
+ context 'with default metadata component' do
120
+ it 'renders metadata' do
121
+ expect(rendered).to have_selector 'dl.document-metadata'
122
+ expect(rendered).to have_selector 'dt', text: 'Title:'
123
+ expect(rendered).to have_selector 'dd', text: 'Title'
124
+ expect(rendered).not_to have_selector 'dt', text: 'ISBN:'
125
+ end
126
+ end
114
127
  end
115
128
 
116
129
  context 'show view' do
117
130
  let(:attr) { { title_component: :h1, show: true } }
118
131
 
119
132
  before do
120
- allow(view_context).to receive(:action_name).and_return('show')
133
+ controller.action_name = "show"
121
134
  end
122
135
 
123
136
  it 'renders with an id' do
@@ -148,13 +161,26 @@ RSpec.describe Blacklight::DocumentComponent, type: :component do
148
161
  blacklight_config.show.embed_component = StubComponent
149
162
  expect(rendered).to have_content 'embed'
150
163
  end
151
- end
152
164
 
153
- it 'renders metadata' do
154
- expect(rendered).to have_selector 'dl.document-metadata'
155
- expect(rendered).to have_selector 'dt', text: 'Title:'
156
- expect(rendered).to have_selector 'dd', text: 'Title'
157
- expect(rendered).not_to have_selector 'dt', text: 'ISBN:'
165
+ context 'with configured metadata component' do
166
+ let(:custom_component_class) do
167
+ Class.new(Blacklight::DocumentMetadataComponent) do
168
+ # Override component rendering with our own value
169
+ def call
170
+ 'blah'
171
+ end
172
+ end
173
+ end
174
+
175
+ before do
176
+ stub_const('MyMetadataComponent', custom_component_class)
177
+ blacklight_config.show.metadata_component = MyMetadataComponent
178
+ end
179
+
180
+ it 'renders custom component' do
181
+ expect(rendered).to have_text 'blah'
182
+ end
183
+ end
158
184
  end
159
185
 
160
186
  it 'renders partials' do
@@ -11,7 +11,24 @@ RSpec.describe Blacklight::Response::ViewTypeComponent, type: :component do
11
11
  let(:search_state) { instance_double(Blacklight::SearchState, to_h: { controller: 'catalog', action: 'index' }) }
12
12
  let(:view_config) { Blacklight::Configuration::ViewConfig.new }
13
13
 
14
+ let(:custom_component_class) do
15
+ Class.new(Blacklight::Icons::IconComponent) do
16
+ # Override component rendering with our own value
17
+ def call
18
+ 'blah'
19
+ end
20
+ end
21
+ end
22
+
23
+ before do
24
+ stub_const('Blacklight::Icons::DefComponent', custom_component_class)
25
+ end
26
+
14
27
  describe "when some views exist" do
28
+ before do
29
+ stub_const('Blacklight::Icons::AbcComponent', custom_component_class)
30
+ end
31
+
15
32
  let(:views) do
16
33
  {
17
34
  abc: view_config,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blacklight
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.0.beta6
4
+ version: 8.0.0.beta8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Rochkind
@@ -17,7 +17,7 @@ authors:
17
17
  autorequire:
18
18
  bindir: exe
19
19
  cert_chain: []
20
- date: 2023-02-21 00:00:00.000000000 Z
20
+ date: 2023-02-27 00:00:00.000000000 Z
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
23
23
  name: rails
@@ -378,6 +378,10 @@ files:
378
378
  - app/assets/config/blacklight/manifest.js
379
379
  - app/assets/images/blacklight/logo.png
380
380
  - app/assets/images/favicon.ico
381
+ - app/assets/javascripts/blacklight/blacklight.esm.js
382
+ - app/assets/javascripts/blacklight/blacklight.esm.js.map
383
+ - app/assets/javascripts/blacklight/blacklight.js
384
+ - app/assets/javascripts/blacklight/blacklight.js.map
381
385
  - app/assets/stylesheets/blacklight/_autocomplete.scss
382
386
  - app/assets/stylesheets/blacklight/_balanced_list.scss
383
387
  - app/assets/stylesheets/blacklight/_blacklight_base.scss