blacklight-spotlight 5.3.2 → 6.0.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -4
  3. data/Rakefile +1 -2
  4. data/app/assets/javascripts/spotlight/spotlight.esm.js +2168 -4560
  5. data/app/assets/javascripts/spotlight/spotlight.esm.js.map +1 -1
  6. data/app/assets/javascripts/spotlight/spotlight.js +2167 -4559
  7. data/app/assets/javascripts/spotlight/spotlight.js.map +1 -1
  8. data/app/assets/stylesheets/spotlight/_blacklight_overrides.scss +4 -0
  9. data/app/assets/stylesheets/spotlight/_browse.scss +0 -4
  10. data/app/assets/stylesheets/spotlight/{_collapse_toggle.scss → _collapse_toggle.css} +2 -2
  11. data/app/assets/stylesheets/spotlight/_exhibit_admin.scss +8 -0
  12. data/app/assets/stylesheets/spotlight/_exhibits_index.scss +2 -1
  13. data/app/assets/stylesheets/spotlight/_footer.scss +2 -2
  14. data/app/assets/stylesheets/spotlight/_header.scss +8 -8
  15. data/app/assets/stylesheets/spotlight/_pages.scss +1 -1
  16. data/app/assets/stylesheets/spotlight/_slideshow_block.scss +15 -0
  17. data/app/assets/stylesheets/spotlight/_spotlight.scss +1 -1
  18. data/app/assets/stylesheets/spotlight/_translations.scss +3 -3
  19. data/app/assets/stylesheets/spotlight/_uploaded_items_block.css +7 -0
  20. data/app/assets/stylesheets/spotlight/_variables.css +15 -0
  21. data/app/assets/stylesheets/spotlight/browse_group_categories_block.scss +0 -76
  22. data/app/components/blacklight/icons/pause_component.rb +15 -0
  23. data/app/components/blacklight/icons/play_arrow_component.rb +15 -0
  24. data/app/components/spotlight/analytics/dashboard_component.html.erb +1 -1
  25. data/app/components/spotlight/custom_field_form_group_component.rb +40 -0
  26. data/app/components/spotlight/document_admin_table_component.html.erb +2 -2
  27. data/app/components/spotlight/edit_sidecar_component.html.erb +18 -0
  28. data/app/components/spotlight/edit_sidecar_component.rb +41 -0
  29. data/app/components/spotlight/masthead_component.html.erb +18 -0
  30. data/app/components/spotlight/masthead_component.rb +46 -0
  31. data/app/components/spotlight/save_search_component.html.erb +1 -1
  32. data/app/components/spotlight/sidecar_form_field_component.html.erb +18 -0
  33. data/app/components/spotlight/sidecar_form_field_component.rb +71 -0
  34. data/app/components/spotlight/solr_document_legacy_embed_component.html.erb +0 -4
  35. data/app/components/spotlight/solr_document_legacy_embed_component.rb +2 -2
  36. data/app/components/spotlight/tag_selector_component.html.erb +1 -1
  37. data/app/components/spotlight/tag_selector_component.rb +3 -2
  38. data/app/components/spotlight/upload_field_form_group_component.rb +33 -0
  39. data/app/controllers/concerns/spotlight/controller.rb +15 -14
  40. data/app/controllers/concerns/spotlight/search_helper.rb +2 -5
  41. data/app/controllers/spotlight/browse_controller.rb +1 -6
  42. data/app/controllers/spotlight/catalog_controller.rb +2 -3
  43. data/app/controllers/spotlight/concerns/application_controller.rb +4 -6
  44. data/app/controllers/spotlight/exhibits_controller.rb +22 -10
  45. data/app/controllers/spotlight/lock_controller.rb +1 -1
  46. data/app/controllers/spotlight/pages_controller.rb +11 -11
  47. data/app/controllers/spotlight/resources/upload_controller.rb +1 -1
  48. data/app/controllers/spotlight/resources_controller.rb +1 -1
  49. data/app/controllers/spotlight/solr_controller.rb +5 -1
  50. data/app/helpers/spotlight/application_helper.rb +14 -26
  51. data/app/helpers/spotlight/crud_link_helpers.rb +0 -12
  52. data/app/helpers/spotlight/main_app_helpers.rb +2 -2
  53. data/app/javascript/spotlight/admin/add_another.js +37 -25
  54. data/app/javascript/spotlight/admin/add_image_selector.js +43 -26
  55. data/app/javascript/spotlight/admin/add_new_button.js +51 -36
  56. data/app/javascript/spotlight/admin/blacklight_configuration.js +76 -54
  57. data/app/javascript/spotlight/admin/block_mixins/autocompleteable.js +102 -101
  58. data/app/javascript/spotlight/admin/block_mixins/formable.js +91 -75
  59. data/app/javascript/spotlight/admin/block_mixins/plustextable.js +46 -48
  60. data/app/javascript/spotlight/admin/blocks/block.js +27 -28
  61. data/app/javascript/spotlight/admin/blocks/browse_block.js +14 -10
  62. data/app/javascript/spotlight/admin/blocks/browse_group_categories_block.js +14 -10
  63. data/app/javascript/spotlight/admin/blocks/pages_block.js +5 -4
  64. data/app/javascript/spotlight/admin/blocks/resources_block.js +61 -41
  65. data/app/javascript/spotlight/admin/blocks/search_result_block.js +24 -7
  66. data/app/javascript/spotlight/admin/blocks/solr_documents_base_block.js +103 -50
  67. data/app/javascript/spotlight/admin/blocks/solr_documents_carousel_block.js +50 -26
  68. data/app/javascript/spotlight/admin/blocks/solr_documents_features_block.js +47 -23
  69. data/app/javascript/spotlight/admin/blocks/uploaded_items_block.js +35 -19
  70. data/app/javascript/spotlight/admin/crop.js +146 -66
  71. data/app/javascript/spotlight/admin/croppable.js +5 -4
  72. data/app/javascript/spotlight/admin/croppable_modal.js +67 -32
  73. data/app/javascript/spotlight/admin/edit_in_place.js +69 -54
  74. data/app/javascript/spotlight/admin/exhibits.js +92 -45
  75. data/app/javascript/spotlight/admin/form_observer.js +92 -78
  76. data/app/javascript/spotlight/admin/multi_image_selector.js +201 -153
  77. data/app/javascript/spotlight/admin/pages.js +13 -7
  78. data/app/javascript/spotlight/admin/progress_monitor.js +65 -50
  79. data/app/javascript/spotlight/admin/readonly_checkbox.js +7 -3
  80. data/app/javascript/spotlight/admin/search_typeahead.js +20 -11
  81. data/app/javascript/spotlight/admin/select_related_input.js +23 -23
  82. data/app/javascript/spotlight/admin/serialize_json.js +120 -0
  83. data/app/javascript/spotlight/admin/sir-trevor/block_controls.js +24 -11
  84. data/app/javascript/spotlight/admin/sir-trevor/block_limits.js +9 -6
  85. data/app/javascript/spotlight/admin/sir-trevor/locales.js +2 -2
  86. data/app/javascript/spotlight/admin/spotlight_nestable.js +2 -5
  87. data/app/javascript/spotlight/admin/translation_progress.js +18 -13
  88. data/app/javascript/spotlight/admin/users.js +151 -64
  89. data/app/javascript/spotlight/user/carousel.js +104 -32
  90. data/app/javascript/spotlight/user/clear_form_button.js +24 -14
  91. data/app/javascript/spotlight/user/index.js +0 -2
  92. data/app/javascript/spotlight/user/zpr_links.js +99 -40
  93. data/app/jobs/concerns/spotlight/gather_documents.rb +1 -1
  94. data/app/jobs/spotlight/add_uploads_from_csv.rb +31 -8
  95. data/app/jobs/spotlight/cleanup_job_trackers_job.rb +5 -3
  96. data/app/models/concerns/spotlight/access_controls_enforcement_search_builder.rb +13 -2
  97. data/app/models/concerns/spotlight/browse_category_search_builder.rb +3 -3
  98. data/app/models/concerns/spotlight/exhibit_documents.rb +2 -2
  99. data/app/models/concerns/spotlight/sitemap.rb +2 -2
  100. data/app/models/concerns/spotlight/solr_document/finder.rb +2 -2
  101. data/app/models/concerns/spotlight/solr_document.rb +1 -47
  102. data/app/models/sir_trevor_rails/blocks/browse_block.rb +0 -4
  103. data/app/models/sir_trevor_rails/blocks/solr_documents_block.rb +2 -1
  104. data/app/models/spotlight/blacklight_configuration.rb +5 -3
  105. data/app/models/spotlight/contact_email.rb +2 -2
  106. data/app/models/spotlight/custom_field.rb +17 -0
  107. data/app/models/spotlight/exhibit.rb +28 -0
  108. data/app/models/spotlight/featured_image.rb +1 -1
  109. data/app/models/spotlight/field_metadata.rb +10 -5
  110. data/app/models/spotlight/job_tracker.rb +0 -18
  111. data/app/models/spotlight/main_navigation.rb +2 -2
  112. data/app/models/spotlight/page.rb +3 -3
  113. data/app/models/spotlight/resources/iiif_harvester.rb +7 -4
  114. data/app/models/spotlight/resources/iiif_manifest.rb +0 -4
  115. data/app/models/spotlight/resources/iiif_manifest_metadata.rb +1 -3
  116. data/app/models/spotlight/resources/iiif_service.rb +21 -6
  117. data/app/models/spotlight/resources/upload.rb +6 -0
  118. data/app/models/spotlight/search.rb +5 -9
  119. data/app/models/spotlight/solr_document_sidecar.rb +18 -0
  120. data/app/services/spotlight/bulk_updates_csv_template_service.rb +1 -1
  121. data/app/services/spotlight/etl/pipeline.rb +2 -2
  122. data/app/services/spotlight/etl/step.rb +2 -2
  123. data/app/views/catalog/_remove_tags.html.erb +2 -2
  124. data/app/views/layouts/spotlight/base.html.erb +3 -6
  125. data/app/views/layouts/spotlight/spotlight.html.erb +2 -2
  126. data/app/views/shared/_analytics.html.erb +4 -3
  127. data/app/views/shared/_masthead.html.erb +2 -18
  128. data/app/views/spotlight/about_pages/_empty.html.erb +1 -1
  129. data/app/views/spotlight/about_pages/_sidebar.html.erb +10 -8
  130. data/app/views/spotlight/browse/_search.html.erb +2 -2
  131. data/app/views/spotlight/browse/_search_box.html.erb +6 -14
  132. data/app/views/spotlight/browse/index.html.erb +1 -1
  133. data/app/views/spotlight/browse/show.html.erb +1 -1
  134. data/app/views/spotlight/catalog/_document_admin_table.html.erb +1 -1
  135. data/app/views/spotlight/catalog/_edit_default.html.erb +1 -1
  136. data/app/views/spotlight/catalog/edit.html.erb +1 -1
  137. data/app/views/spotlight/dashboards/_page.html.erb +1 -1
  138. data/app/views/spotlight/exhibits/_exhibit_card.html.erb +3 -1
  139. data/app/views/spotlight/exhibits/_export.html.erb +17 -1
  140. data/app/views/spotlight/exhibits/_form.html.erb +20 -4
  141. data/app/views/spotlight/exhibits/_missing_exhibits.html.erb +3 -3
  142. data/app/views/spotlight/exhibits/index.html.erb +21 -8
  143. data/app/views/spotlight/feature_pages/_empty.html.erb +1 -1
  144. data/app/views/spotlight/feature_pages/_sidebar.html.erb +17 -15
  145. data/app/views/spotlight/home_pages/_empty.html.erb +7 -7
  146. data/app/views/spotlight/pages/show.html.erb +1 -0
  147. data/app/views/spotlight/resources/upload/_form.html.erb +2 -2
  148. data/app/views/spotlight/sir_trevor/blocks/_browse_group_categories_block.html.erb +5 -16
  149. data/app/views/spotlight/sir_trevor/blocks/_embedded_document.html.erb +3 -5
  150. data/app/views/spotlight/sir_trevor/blocks/_link_to_search_block.html.erb +1 -1
  151. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_carousel_block.html.erb +11 -2
  152. data/app/views/spotlight/sites/_exhibit.html.erb +1 -1
  153. data/config/i18n-tasks.yml +1 -1
  154. data/config/locales/spotlight.ar.yml +1 -2
  155. data/config/locales/spotlight.de.yml +1 -2
  156. data/config/locales/spotlight.en.yml +39 -10
  157. data/config/locales/spotlight.es.yml +1 -2
  158. data/config/locales/spotlight.fr.yml +1 -2
  159. data/config/locales/spotlight.it.yml +1 -2
  160. data/config/locales/spotlight.ja.yml +39 -0
  161. data/config/locales/spotlight.zh.yml +1 -2
  162. data/db/migrate/20261007133748_add_discovery_enabled_to_spotlight_exhibits.rb +5 -0
  163. data/lib/generators/spotlight/assets/importmap_generator.rb +2 -10
  164. data/lib/generators/spotlight/assets/propshaft_generator.rb +5 -4
  165. data/lib/generators/spotlight/install_generator.rb +2 -4
  166. data/lib/generators/spotlight/templates/catalog_controller.rb +0 -1
  167. data/lib/generators/spotlight/templates/config/initializers/spotlight_initializer.rb +26 -4
  168. data/lib/generators/spotlight/templates/solr/conf/schema.xml +4 -3
  169. data/lib/generators/spotlight/templates/solr/conf/solrconfig.xml +3 -8
  170. data/lib/migration/page_language.rb +0 -2
  171. data/lib/spotlight/engine.rb +28 -9
  172. data/lib/spotlight/search_state.rb +7 -11
  173. data/lib/spotlight/upload_field_config.rb +19 -1
  174. data/lib/spotlight/version.rb +1 -1
  175. data/lib/spotlight.rb +1 -1
  176. data/spec/support/features/capybara_wait_metadata_helper.rb +3 -1
  177. data/spec/support/features/test_features_helpers.rb +24 -1
  178. data/spec/support/iiif_manifest_fixtures.rb +33 -0
  179. data/spec/support/stub_dns_resolution.rb +12 -0
  180. metadata +50 -80
  181. data/app/assets/stylesheets/spotlight/_uploaded_items_block.scss +0 -7
  182. data/app/assets/stylesheets/spotlight/_variables.scss +0 -13
  183. data/app/components/blacklight/icons/arrow_alt_circle_left_component.rb +0 -15
  184. data/app/components/blacklight/icons/arrow_alt_circle_right_component.rb +0 -15
  185. data/app/components/blacklight/icons/info_component.rb +0 -15
  186. data/app/components/blacklight/icons/zoom_in_component.rb +0 -15
  187. data/app/components/blacklight/icons/zoom_out_component.rb +0 -15
  188. data/app/components/spotlight/icon_component.rb +0 -13
  189. data/app/javascript/spotlight/user/browse_group_categories.js +0 -54
  190. data/app/mailers/spotlight/invitation_mailer.rb +0 -21
  191. data/app/models/concerns/spotlight/resources/web.rb +0 -38
  192. data/app/services/spotlight/iiif_resource_resolver.rb +0 -78
  193. data/app/views/catalog/_image_default.html.erb +0 -6
  194. data/app/views/shared/_exhibit_navbar.html.erb +0 -2
  195. data/app/views/shared/_header_navbar.html.erb +0 -6
  196. data/app/views/spotlight/catalog/_admin_index_header_default.html.erb +0 -23
  197. data/app/views/spotlight/catalog/_admin_thumbnail_default.html.erb +0 -7
  198. data/app/views/spotlight/catalog/_curation_mode_toggle_default.html.erb +0 -1
  199. data/app/views/spotlight/catalog/_document_row.html.erb +0 -2
  200. data/app/views/spotlight/catalog/_edit_sidecar.html.erb +0 -24
  201. data/app/views/spotlight/catalog/_index_compact_default.html.erb +0 -21
  202. data/app/views/spotlight/custom_fields/form_group/_text.html.erb +0 -23
  203. data/app/views/spotlight/custom_fields/form_group/_vocab.html.erb +0 -23
  204. data/app/views/spotlight/invitation_mailer/exhibit_invitation_notification.html.erb +0 -12
  205. data/lib/generators/spotlight/templates/javascript/jquery-shim.js +0 -1
  206. data/vendor/assets/javascripts/tiny-slider.js +0 -3221
  207. data/vendor/assets/stylesheets/tiny-slider.css +0 -1
  208. /data/app/assets/stylesheets/spotlight/{_breadcrumbs.scss → _breadcrumbs.css} +0 -0
  209. /data/app/assets/stylesheets/spotlight/{_croppable.scss → _croppable.css} +0 -0
  210. /data/app/assets/stylesheets/spotlight/{_modals.scss → _modals.css} +0 -0
  211. /data/app/assets/stylesheets/spotlight/{_utilities.scss → _utilities.css} +0 -0
@@ -1,81 +1,96 @@
1
1
  export default class {
2
2
  connect() {
3
- $("[data-expanded-add-button]").each((_i, el) =>
4
- this.addExpandBehaviorToButton($(el)),
5
- )
3
+ document
4
+ .querySelectorAll("[data-expanded-add-button]")
5
+ .forEach((el) => this.addExpandBehaviorToButton(el))
6
6
  }
7
7
 
8
8
  addExpandBehaviorToButton(button) {
9
9
  var settings = {
10
- speed: button.data("speed") || 450,
11
- animate_width: button.data("animate_width") || 425,
10
+ speed: parseInt(button.dataset.speed || "450", 10),
11
+ animate_width: parseInt(button.dataset.animateWidth || "425", 10),
12
12
  }
13
- var target = $(button.data("field-target"))
14
- var save = $("input[data-behavior='save']", target)
15
- var cancel = $("input[data-behavior='cancel']", target)
16
- var input = $("input[type='text']", target)
17
- var original_width = button.outerWidth()
13
+ var target = document.querySelector(button.dataset.fieldTarget)
14
+ var save = target.querySelector("input[data-behavior='save']")
15
+ var cancel = target.querySelector("input[data-behavior='cancel']")
16
+ var input = target.querySelector("input[type='text']")
17
+ var original_width = button.offsetWidth
18
18
  var expanded = false
19
19
 
20
20
  // Animate button open when the mouse enters or
21
21
  // the button is given focus (i.e. clicked/tabbed)
22
- button.on("mouseenter focus", function () {
23
- expandButton()
24
- })
22
+ button.addEventListener("mouseenter", expandButton)
23
+ button.addEventListener("focus", expandButton)
25
24
 
26
25
  // Don't allow blank titles
27
- save.on("click", function () {
26
+ save.addEventListener("click", function (e) {
28
27
  if (inputEmpty()) {
29
- return false
28
+ e.preventDefault()
29
+ e.stopPropagation()
30
30
  }
31
31
  })
32
32
 
33
33
  // Empty input and collapse
34
34
  // button on cancel click
35
- cancel.on("click", function (e) {
35
+ cancel.addEventListener("click", function (e) {
36
36
  e.preventDefault()
37
- input.val("")
37
+ input.value = ""
38
38
  collapseButton()
39
39
  })
40
40
 
41
41
  // Collapse the button on when
42
42
  // an empty input loses focus
43
- input.on("blur", function () {
43
+ input.addEventListener("blur", function () {
44
44
  if (inputEmpty()) {
45
45
  collapseButton()
46
46
  }
47
47
  })
48
+
48
49
  function expandButton() {
49
50
  // If this has not yet been expanded, recalculate original_width to
50
51
  // handle things that may have been originally hidden.
51
52
  if (!expanded) {
52
- original_width = button.outerWidth()
53
+ original_width = button.offsetWidth
53
54
  }
54
- if (button.outerWidth() <= original_width + 5) {
55
+ if (button.offsetWidth <= original_width + 5) {
55
56
  expanded = true
56
- button.animate(
57
+ var anim = button.animate(
57
58
  { width: settings.animate_width + "px" },
58
- settings.speed,
59
- function () {
60
- target.show(0, function () {
61
- input.focus()
62
- // Set the button to auto width to make
63
- // sure it has room for any inputs
64
- button.width("auto")
65
- // Explicitly set the width of the button
66
- // so the close animation works properly
67
- button.width(button.width())
68
- })
69
- },
59
+ { duration: settings.speed },
70
60
  )
61
+ anim.onfinish = function () {
62
+ button.style.width = settings.animate_width + "px"
63
+ showElement(target)
64
+ input.focus()
65
+ // Set the button to auto width to make
66
+ // sure it has room for any inputs
67
+ button.style.width = "auto"
68
+ // Explicitly set the width of the button
69
+ // so the close animation works properly
70
+ button.style.width = button.offsetWidth + "px"
71
+ }
71
72
  }
72
73
  }
73
74
  function collapseButton() {
74
- target.hide()
75
- button.animate({ width: original_width + "px" }, settings.speed)
75
+ target.style.display = "none"
76
+ var anim = button.animate(
77
+ { width: original_width + "px" },
78
+ { duration: settings.speed },
79
+ )
80
+ anim.onfinish = function () {
81
+ button.style.width = original_width + "px"
82
+ }
83
+ }
84
+ // Show an element that may be hidden via a CSS class by overriding with an
85
+ // appropriate inline display value (mirrors jQuery's .show()).
86
+ function showElement(el) {
87
+ el.style.display = ""
88
+ if (window.getComputedStyle(el).display === "none") {
89
+ el.style.display = "inline-block"
90
+ }
76
91
  }
77
92
  function inputEmpty() {
78
- return $.trim(input.val()) == ""
93
+ return input.value.trim() == ""
79
94
  }
80
95
  }
81
96
  }
@@ -7,70 +7,92 @@ export default class {
7
7
 
8
8
  // Add Select/Deselect all behavior for metadata field names for a given view e.g. Item details.
9
9
  addCheckboxToggleBehavior() {
10
- $("[data-behavior='metadata-select']").each(function () {
11
- var selectCheckbox = $(this)
12
- var parentCell = selectCheckbox.parents("th")
13
- var table = parentCell.closest("table")
14
- var columnRows = $(
15
- "tr td:nth-child(" + (parentCell.index() + 1) + ")",
16
- table,
17
- )
18
- var checkboxes = $("input[type='checkbox']", columnRows)
19
- updateSelectAllInput(selectCheckbox, columnRows)
20
- // Add the check/uncheck behavior to the select/deselect all checkbox
21
- selectCheckbox.on("click", function () {
22
- var allChecked = allCheckboxesChecked(columnRows)
23
- columnRows.each(function () {
24
- $("input[type='checkbox']", $(this)).prop("checked", !allChecked)
25
- })
26
- })
27
- // When a single checkbox is selected/unselected, the "All" checkbox should be updated accordingly.
28
- checkboxes.each(function () {
29
- $(this).on("change", function () {
30
- updateSelectAllInput(selectCheckbox, columnRows)
31
- })
32
- })
33
- })
34
-
35
10
  // Check number of checkboxes against the number of checked
36
11
  // checkboxes to determine if all of them are checked or not
37
- function allCheckboxesChecked(elements) {
38
- return (
39
- $("input[type='checkbox']", elements).length ==
40
- $("input[type='checkbox']:checked", elements).length
41
- )
12
+ function allCheckboxesChecked(cells) {
13
+ let total = 0
14
+ let checked = 0
15
+ cells.forEach((cell) => {
16
+ cell.querySelectorAll("input[type='checkbox']").forEach((cb) => {
17
+ total++
18
+ if (cb.checked) {
19
+ checked++
20
+ }
21
+ })
22
+ })
23
+ return total === checked
42
24
  }
43
25
 
44
26
  // Check or uncheck the "All" checkbox for each view column, e.g. Item details, List, etc.
45
- function updateSelectAllInput(checkbox, elements) {
46
- if (allCheckboxesChecked(elements)) {
47
- checkbox.prop("checked", true)
48
- } else {
49
- checkbox.prop("checked", false)
50
- }
27
+ function updateSelectAllInput(checkbox, cells) {
28
+ checkbox.checked = allCheckboxesChecked(cells)
51
29
  }
30
+
31
+ document
32
+ .querySelectorAll("[data-behavior='metadata-select']")
33
+ .forEach((selectCheckbox) => {
34
+ const parentCell = selectCheckbox.closest("th")
35
+ if (!parentCell) return
36
+
37
+ const table = parentCell.closest("table")
38
+ if (!table) return
39
+
40
+ const columnIndex = Array.from(parentCell.parentNode.children).indexOf(
41
+ parentCell,
42
+ )
43
+ const columnRows = table.querySelectorAll(
44
+ `tr td:nth-child(${columnIndex + 1})`,
45
+ )
46
+
47
+ const checkboxes = []
48
+ columnRows.forEach((cell) => {
49
+ cell.querySelectorAll("input[type='checkbox']").forEach((cb) => {
50
+ checkboxes.push(cb)
51
+ })
52
+ })
53
+
54
+ updateSelectAllInput(selectCheckbox, columnRows)
55
+
56
+ // Add the check/uncheck behavior to the select/deselect all checkbox
57
+ selectCheckbox.addEventListener("click", () => {
58
+ const allChecked = allCheckboxesChecked(columnRows)
59
+ columnRows.forEach((cell) => {
60
+ cell.querySelectorAll("input[type='checkbox']").forEach((cb) => {
61
+ cb.checked = !allChecked
62
+ cb.dispatchEvent(new Event("change", { bubbles: true }))
63
+ })
64
+ })
65
+ updateSelectAllInput(selectCheckbox, columnRows)
66
+ })
67
+
68
+ // When a single checkbox is selected/unselected, the "All" checkbox should be updated accordingly.
69
+ checkboxes.forEach((cb) => {
70
+ cb.addEventListener("change", () => {
71
+ updateSelectAllInput(selectCheckbox, columnRows)
72
+ })
73
+ })
74
+ })
52
75
  }
53
76
 
54
77
  addEnableToggleBehavior() {
55
- $("[data-behavior='enable-feature']").each(function () {
56
- var checkbox = $(this)
57
- var target = $($(this).data("target"))
78
+ document
79
+ .querySelectorAll("[data-behavior='enable-feature']")
80
+ .forEach((checkbox) => {
81
+ const targetSelector = checkbox.dataset.target
82
+ if (!targetSelector) return
83
+ const target = document.querySelector(targetSelector)
84
+ if (!target) return
58
85
 
59
- checkbox.on("change", function () {
60
- if ($(this).is(":checked")) {
61
- target
62
- .find("input:checkbox")
63
- .not("[data-behavior='enable-feature']")
64
- .prop("checked", true)
65
- .attr("disabled", false)
66
- } else {
67
- target
68
- .find("input:checkbox")
69
- .not("[data-behavior='enable-feature']")
70
- .prop("checked", false)
71
- .attr("disabled", true)
72
- }
86
+ checkbox.addEventListener("change", () => {
87
+ const isChecked = checkbox.checked
88
+ target.querySelectorAll("input[type='checkbox']").forEach((cb) => {
89
+ if (!cb.matches("[data-behavior='enable-feature']")) {
90
+ cb.checked = isChecked
91
+ cb.disabled = !isChecked
92
+ cb.dispatchEvent(new Event("change", { bubbles: true }))
93
+ }
94
+ })
95
+ })
73
96
  })
74
- })
75
97
  }
76
98
  }
@@ -1,112 +1,113 @@
1
1
  import { fetchAutocompleteJSON } from "spotlight/admin/search_typeahead"
2
- ;(function ($) {
3
- ;((SirTrevor.BlockMixins.Autocompleteable = {
4
- mixinName: "Autocompleteable",
5
- preload: true,
6
-
7
- initializeAutocompleteable: function () {
8
- this.on("onRender", this.addAutocompletetoSirTrevorForm)
9
-
10
- if (this["autocomplete_url"] === undefined) {
11
- this.autocomplete_url = function () {
12
- return $("form[data-autocomplete-url]").data("autocomplete-url")
13
- }
14
- }
15
2
 
16
- if (this["autocomplete_fetch"] === undefined) {
17
- this.autocomplete_fetch = this.fetchAutocompleteResults
18
- }
3
+ SirTrevor.BlockMixins.Autocompleteable = {
4
+ mixinName: "Autocompleteable",
5
+ preload: true,
19
6
 
20
- if (this["transform_autocomplete_results"] === undefined) {
21
- this.transform_autocomplete_results = (val) => val
7
+ initializeAutocompleteable: function () {
8
+ this.on("onRender", this.addAutocompletetoSirTrevorForm)
9
+
10
+ if (this["autocomplete_url"] === undefined) {
11
+ this.autocomplete_url = function () {
12
+ var el = document.querySelector("form[data-autocomplete-url]")
13
+ return el ? el.dataset.autocompleteUrl : undefined
22
14
  }
15
+ }
16
+
17
+ if (this["autocomplete_fetch"] === undefined) {
18
+ this.autocomplete_fetch = this.fetchAutocompleteResults
19
+ }
23
20
 
24
- if (this["highlight"] === undefined) {
25
- this.highlight = function (value) {
26
- if (!value) return ""
27
- const queryValue = this.getQueryValue().trim()
28
- return queryValue
29
- ? value.replace(new RegExp(queryValue, "gi"), "<strong>$&</strong>")
30
- : value
31
- }
21
+ if (this["transform_autocomplete_results"] === undefined) {
22
+ this.transform_autocomplete_results = (val) => val
23
+ }
24
+
25
+ if (this["highlight"] === undefined) {
26
+ this.highlight = function (value) {
27
+ if (!value) return ""
28
+ const queryValue = this.getQueryValue().trim()
29
+ return queryValue
30
+ ? value.replace(new RegExp(queryValue, "gi"), "<strong>$&</strong>")
31
+ : value
32
32
  }
33
+ }
33
34
 
34
- if (this["autocomplete_control"] === undefined) {
35
- this.autocomplete_control = function () {
36
- const autocompleteID = this.autocompleteID()
37
- return `
38
- <auto-complete src="${this.autocomplete_url()}" for="${autocompleteID}-popup" fetch-on-empty>
39
- <input type="text" name="${autocompleteID}" placeholder="${i18n.t("blocks:autocompleteable:placeholder")}" data-default-typeahead>
40
- <ul id="${autocompleteID}-popup"></ul>
41
- <div id="${autocompleteID}-popup-feedback" class="visually-hidden"></div>
42
- </auto-complete>
43
- `
44
- }
35
+ if (this["autocomplete_control"] === undefined) {
36
+ this.autocomplete_control = function () {
37
+ const autocompleteID = this.autocompleteID()
38
+ return `
39
+ <auto-complete src="${this.autocomplete_url()}" for="${autocompleteID}-popup" fetch-on-empty>
40
+ <input type="text" name="${autocompleteID}" placeholder="${i18n.t("blocks:autocompleteable:placeholder")}" data-default-typeahead>
41
+ <ul id="${autocompleteID}-popup"></ul>
42
+ <div id="${autocompleteID}-popup-feedback" class="visually-hidden"></div>
43
+ </auto-complete>
44
+ `
45
45
  }
46
+ }
46
47
 
47
- if (this["autocomplete_element_template"] === undefined) {
48
- this.autocomplete_element_template = function (item) {
49
- return `<li role="option" data-autocomplete-value="${item.id}">${this.autocomplete_template(item)}</li>`
50
- }
48
+ if (this["autocomplete_element_template"] === undefined) {
49
+ this.autocomplete_element_template = function (item) {
50
+ return `<li role="option" data-autocomplete-value="${item.id}">${this.autocomplete_template(item)}</li>`
51
51
  }
52
- },
53
-
54
- queryTokenizer: function (query) {
55
- return query.trim().toLowerCase().split(/\s+/).filter(Boolean)
56
- },
57
-
58
- filterResults: function (data, query) {
59
- const queryStrings = this.queryTokenizer(query)
60
- return data.filter((item) => {
61
- const lowerTitle = item.title.toLowerCase()
62
- return queryStrings.some((queryString) =>
63
- lowerTitle.includes(queryString),
64
- )
65
- })
66
- },
67
-
68
- fetchAutocompleteResults: async function (url) {
69
- const result = await fetchAutocompleteJSON(url)
70
- const transformed = this.transform_autocomplete_results(result)
71
- this.fetchedData = {}
72
- transformed.map((item) => (this.fetchedData[item.id] = item))
73
- return transformed
74
- .map((item) => this.autocomplete_element_template(item))
75
- .join("")
76
- },
77
-
78
- fetchOnceAndFilterLocalResults: async function (url) {
79
- if (this.fetchedData === undefined) {
80
- await this.fetchAutocompleteResults(url)
52
+ }
53
+ },
54
+
55
+ queryTokenizer: function (query) {
56
+ return query.trim().toLowerCase().split(/\s+/).filter(Boolean)
57
+ },
58
+
59
+ filterResults: function (data, query) {
60
+ const queryStrings = this.queryTokenizer(query)
61
+ return data.filter((item) => {
62
+ const lowerTitle = item.title.toLowerCase()
63
+ return queryStrings.some((queryString) =>
64
+ lowerTitle.includes(queryString),
65
+ )
66
+ })
67
+ },
68
+
69
+ fetchAutocompleteResults: async function (url) {
70
+ const result = await fetchAutocompleteJSON(url)
71
+ const transformed = this.transform_autocomplete_results(result)
72
+ this.fetchedData = {}
73
+ transformed.map((item) => (this.fetchedData[item.id] = item))
74
+ return transformed
75
+ .map((item) => this.autocomplete_element_template(item))
76
+ .join("")
77
+ },
78
+
79
+ fetchOnceAndFilterLocalResults: async function (url) {
80
+ if (this.fetchedData === undefined) {
81
+ await this.fetchAutocompleteResults(url)
82
+ }
83
+ const query = url.searchParams.get("q")
84
+ const data = Object.values(this.fetchedData)
85
+ const filteredData = query ? this.filterResults(data, query) : data
86
+ return filteredData
87
+ .map((item) => this.autocomplete_element_template(item))
88
+ .join("")
89
+ },
90
+
91
+ autocompleteID: function () {
92
+ return this.blockID + "-autocomplete"
93
+ },
94
+
95
+ getQueryValue: function () {
96
+ const completer = this.inner.querySelector("auto-complete > input")
97
+ return completer.value
98
+ },
99
+
100
+ addAutocompletetoSirTrevorForm: function () {
101
+ const completer = this.inner.querySelector("auto-complete")
102
+ completer.fetchResult = this.autocomplete_fetch.bind(this)
103
+ completer.addEventListener("auto-complete-change", (e) => {
104
+ const data = this.fetchedData[e.relatedTarget.value]
105
+ if (e.relatedTarget.value && data) {
106
+ e.value = e.relatedTarget.value = ""
107
+ this.createItemPanel({ ...data, display: "true" })
81
108
  }
82
- const query = url.searchParams.get("q")
83
- const data = Object.values(this.fetchedData)
84
- const filteredData = query ? this.filterResults(data, query) : data
85
- return filteredData
86
- .map((item) => this.autocomplete_element_template(item))
87
- .join("")
88
- },
89
-
90
- autocompleteID: function () {
91
- return this.blockID + "-autocomplete"
92
- },
93
-
94
- getQueryValue: function () {
95
- const completer = this.inner.querySelector("auto-complete > input")
96
- return completer.value
97
- },
98
-
99
- addAutocompletetoSirTrevorForm: function () {
100
- const completer = this.inner.querySelector("auto-complete")
101
- completer.fetchResult = this.autocomplete_fetch.bind(this)
102
- completer.addEventListener("auto-complete-change", (e) => {
103
- const data = this.fetchedData[e.relatedTarget.value]
104
- if (e.relatedTarget.value && data) {
105
- e.value = e.relatedTarget.value = ""
106
- this.createItemPanel({ ...data, display: "true" })
107
- }
108
- })
109
- },
110
- }),
111
- SirTrevor.Block.prototype.availableMixins.push("autocompleteable"))
112
- })(jQuery)
109
+ })
110
+ },
111
+ }
112
+
113
+ SirTrevor.Block.prototype.availableMixins.push("autocompleteable")
@@ -1,86 +1,102 @@
1
- ;(function ($) {
2
- ;((SirTrevor.BlockMixins.Formable = {
3
- mixinName: "Formable",
4
- preload: true,
5
-
6
- initializeFormable: function () {
7
- if (this["afterLoadData"] === undefined) {
8
- this["afterLoadData"] = function (_data) {}
9
- }
10
- },
1
+ import serializeJSON from "spotlight/admin/serialize_json"
2
+
3
+ function setElementValue(el, values) {
4
+ var type = el.type || ""
5
+ if (type === "checkbox" || type === "radio") {
6
+ el.checked = values.indexOf(el.value) !== -1
7
+ } else if (el.nodeName.toLowerCase() === "select") {
8
+ Array.prototype.forEach.call(el.options, function (opt) {
9
+ opt.selected = values.indexOf(opt.value) !== -1
10
+ })
11
+ } else {
12
+ el.value = values[0] != null ? values[0] : ""
13
+ }
14
+ }
15
+
16
+ SirTrevor.BlockMixins.Formable = {
17
+ mixinName: "Formable",
18
+ preload: true,
19
+
20
+ initializeFormable: function () {
21
+ if (this["afterLoadData"] === undefined) {
22
+ this["afterLoadData"] = function (_data) {}
23
+ }
24
+ },
11
25
 
12
- formId: function (id) {
13
- return this.blockID + "_" + id
14
- },
26
+ formId: function (id) {
27
+ return this.blockID + "_" + id
28
+ },
15
29
 
16
- _serializeData: function () {
17
- var data = $(":input,textarea,select", this.inner)
18
- .not(":input:radio")
19
- .serializeJSON()
30
+ _serializeData: function () {
31
+ var inputs = this.inner.querySelectorAll("input, select, textarea")
32
+ var nonRadioInputs = Array.prototype.filter.call(inputs, function (el) {
33
+ return el.type !== "radio"
34
+ })
35
+ var data = serializeJSON(nonRadioInputs)
20
36
 
21
- $(":input:radio:checked", this.inner).each(function (index, input) {
22
- var key = $(input).data("key") || input.getAttribute("name")
37
+ this.inner
38
+ .querySelectorAll("input[type='radio']:checked")
39
+ .forEach(function (input) {
40
+ var key = input.getAttribute("data-key") || input.getAttribute("name")
23
41
 
24
- if (!key.match("\\[")) {
25
- data[key] = $(input).val()
42
+ if (key && !/\[/.test(key)) {
43
+ data[key] = input.value
26
44
  }
27
45
  })
28
46
 
29
- /* Simple to start. Add conditions later */
30
- if (this.hasTextBlock()) {
31
- data.text = this.getTextBlockHTML()
32
- data.format = "html"
33
- if (
34
- data.text &&
35
- data.text.length > 0 &&
36
- this.options.convertToMarkdown
37
- ) {
38
- data.text = stToMarkdown(data.text, this.type)
39
- data.format = "markdown"
40
- }
47
+ /* Simple to start. Add conditions later */
48
+ if (this.hasTextBlock()) {
49
+ data.text = this.getTextBlockHTML()
50
+ data.format = "html"
51
+ if (data.text && data.text.length > 0 && this.options.convertToMarkdown) {
52
+ data.text = stToMarkdown(data.text, this.type)
53
+ data.format = "markdown"
54
+ }
55
+ }
56
+
57
+ return data
58
+ },
59
+
60
+ loadData: function (data) {
61
+ if (this.hasTextBlock()) {
62
+ if (
63
+ data.text &&
64
+ data.text.length > 0 &&
65
+ this.options.convertFromMarkdown &&
66
+ data.format !== "html"
67
+ ) {
68
+ this.setTextBlockHTML(SirTrevor.toHTML(data.text, this.type))
69
+ } else {
70
+ this.setTextBlockHTML(data.text)
41
71
  }
72
+ }
73
+ this.loadFormDataByKey(data)
74
+ this.afterLoadData(data)
75
+ },
76
+
77
+ loadFormDataByKey: function (data) {
78
+ var elements = this.inner.querySelectorAll("input, select, textarea")
79
+ Array.prototype.forEach.call(elements, function (input) {
80
+ var type = input.type || ""
81
+ if (type === "button" || type === "submit" || type === "hidden") return
42
82
 
43
- return data
44
- },
45
-
46
- loadData: function (data) {
47
- if (this.hasTextBlock()) {
48
- if (
49
- data.text &&
50
- data.text.length > 0 &&
51
- this.options.convertFromMarkdown &&
52
- data.format !== "html"
53
- ) {
54
- this.setTextBlockHTML(SirTrevor.toHTML(data.text, this.type))
55
- } else {
56
- this.setTextBlockHTML(data.text)
83
+ var key = input.getAttribute("data-key") || input.getAttribute("name")
84
+
85
+ if (key) {
86
+ if (/\[\]$/.test(key)) {
87
+ key = key.replace("[]", "")
57
88
  }
89
+
90
+ // by wrapping it in an array, this'll "just work" for radio and checkbox fields too
91
+ var input_data = data[key]
92
+
93
+ if (!(input_data instanceof Array)) {
94
+ input_data = [input_data]
95
+ }
96
+ setElementValue(input, input_data)
58
97
  }
59
- this.loadFormDataByKey(data)
60
- this.afterLoadData(data)
61
- },
62
-
63
- loadFormDataByKey: function (data) {
64
- $(":input", this.inner)
65
- .not("button,:input[type=hidden]")
66
- .each(function (index, input) {
67
- var key = $(input).data("key") || input.getAttribute("name")
68
-
69
- if (key) {
70
- if (key.match("\\[\\]$")) {
71
- key = key.replace("[]", "")
72
- }
73
-
74
- // by wrapping it in an array, this'll "just work" for radio and checkbox fields too
75
- var input_data = data[key]
76
-
77
- if (!(input_data instanceof Array)) {
78
- input_data = [input_data]
79
- }
80
- $(this).val(input_data)
81
- }
82
- })
83
- },
84
- }),
85
- SirTrevor.Block.prototype.availableMixins.push("formable"))
86
- })(jQuery)
98
+ })
99
+ },
100
+ }
101
+
102
+ SirTrevor.Block.prototype.availableMixins.push("formable")