blacklight 7.41.0 → 8.0.0.beta1

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 (425) hide show
  1. checksums.yaml +4 -4
  2. data/.env +2 -3
  3. data/.github/workflows/ruby.yml +81 -0
  4. data/.rubocop.yml +243 -14
  5. data/.rubocop_todo.yml +137 -429
  6. data/Gemfile +11 -6
  7. data/README.md +11 -3
  8. data/VERSION +1 -1
  9. data/app/assets/javascripts/blacklight/blacklight.esm.js +384 -0
  10. data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -0
  11. data/app/assets/javascripts/blacklight/blacklight.js +374 -493
  12. data/app/assets/javascripts/blacklight/blacklight.js.map +1 -0
  13. data/app/assets/stylesheets/blacklight/_autocomplete.scss +25 -0
  14. data/app/assets/stylesheets/blacklight/_blacklight_base.scss +1 -1
  15. data/app/assets/stylesheets/blacklight/_bookmark.scss +16 -0
  16. data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +8 -0
  17. data/app/assets/stylesheets/blacklight/_constraints.scss +4 -4
  18. data/app/assets/stylesheets/blacklight/_facets.scss +72 -44
  19. data/app/assets/stylesheets/blacklight/_header.scss +0 -15
  20. data/app/assets/stylesheets/blacklight/_icons.scss +0 -14
  21. data/app/assets/stylesheets/blacklight/_mixins.scss +20 -0
  22. data/app/assets/stylesheets/blacklight/_modal.scss +8 -2
  23. data/app/assets/stylesheets/blacklight/_search_form.scss +30 -3
  24. data/app/assets/stylesheets/blacklight/_search_history.scss +10 -1
  25. data/app/assets/stylesheets/blacklight/_search_results.scss +6 -2
  26. data/app/assets/stylesheets/blacklight/blacklight_defaults.scss +4 -1
  27. data/app/builders/blacklight/action_builder.rb +18 -9
  28. data/app/components/blacklight/advanced_search_form_component.html.erb +3 -3
  29. data/app/components/blacklight/advanced_search_form_component.rb +8 -10
  30. data/app/components/blacklight/constraints_component.rb +27 -37
  31. data/app/components/blacklight/document/action_component.rb +12 -10
  32. data/app/components/blacklight/document/actions_component.rb +2 -2
  33. data/app/components/blacklight/document/bookmark_component.html.erb +9 -0
  34. data/app/components/blacklight/document/bookmark_component.rb +4 -2
  35. data/app/components/blacklight/document/citation_component.rb +5 -3
  36. data/app/components/blacklight/document/group_component.rb +7 -3
  37. data/app/components/blacklight/document/sidebar_component.html.erb +2 -0
  38. data/app/components/blacklight/document/sidebar_component.rb +16 -0
  39. data/app/components/blacklight/document/thumbnail_component.html.erb +2 -7
  40. data/app/components/blacklight/document/thumbnail_component.rb +1 -9
  41. data/app/components/blacklight/document_component.html.erb +1 -1
  42. data/app/components/blacklight/document_component.rb +16 -78
  43. data/app/components/blacklight/document_metadata_component.html.erb +2 -4
  44. data/app/components/blacklight/document_metadata_component.rb +5 -10
  45. data/app/components/blacklight/facet_component.rb +3 -3
  46. data/app/components/blacklight/facet_field_checkboxes_component.html.erb +4 -4
  47. data/app/components/blacklight/facet_field_checkboxes_component.rb +1 -1
  48. data/app/components/blacklight/facet_field_component.html.erb +4 -5
  49. data/app/components/blacklight/facet_field_component.rb +9 -2
  50. data/app/components/blacklight/facet_field_list_component.html.erb +3 -3
  51. data/app/components/blacklight/facet_field_list_component.rb +37 -5
  52. data/app/components/blacklight/facet_field_no_layout_component.rb +0 -2
  53. data/app/components/blacklight/facet_item_component.rb +1 -43
  54. data/app/components/blacklight/facet_item_pivot_component.rb +21 -23
  55. data/app/components/blacklight/header_component.rb +1 -1
  56. data/app/components/blacklight/hidden_search_state_component.rb +1 -2
  57. data/app/components/blacklight/icons/icon_component.rb +4 -9
  58. data/app/components/blacklight/icons/legacy_icon_component.rb +30 -0
  59. data/app/components/blacklight/icons/list_component.rb +16 -0
  60. data/app/components/blacklight/icons/search_component.rb +16 -0
  61. data/app/components/blacklight/metadata_field_component.html.erb +2 -2
  62. data/app/components/blacklight/metadata_field_component.rb +24 -8
  63. data/app/components/blacklight/metadata_field_layout_component.rb +4 -25
  64. data/app/components/blacklight/response/facet_group_component.html.erb +3 -5
  65. data/app/components/blacklight/response/facet_group_component.rb +29 -8
  66. data/app/components/blacklight/response/pagination_component.html.erb +1 -1
  67. data/app/components/blacklight/response/pagination_component.rb +2 -11
  68. data/app/components/blacklight/response/sort_component.html.erb +6 -1
  69. data/app/components/blacklight/response/sort_component.rb +1 -16
  70. data/app/components/blacklight/response/spellcheck_component.rb +18 -7
  71. data/app/components/blacklight/response/view_type_button_component.rb +3 -7
  72. data/app/components/blacklight/response/view_type_component.rb +4 -6
  73. data/app/components/blacklight/search/sidebar_component.html.erb +8 -0
  74. data/app/components/blacklight/search/sidebar_component.rb +17 -0
  75. data/app/components/blacklight/search_bar_component.html.erb +20 -15
  76. data/app/components/blacklight/search_bar_component.rb +2 -16
  77. data/app/components/blacklight/search_button_component.rb +3 -3
  78. data/app/components/blacklight/search_context_component.rb +43 -10
  79. data/app/components/blacklight/search_header_component.html.erb +2 -0
  80. data/app/components/blacklight/search_header_component.rb +6 -0
  81. data/app/components/blacklight/start_over_button_component.rb +5 -3
  82. data/app/components/blacklight/system/dropdown_component.rb +8 -5
  83. data/app/components/blacklight/system/flash_message_component.html.erb +4 -2
  84. data/app/components/blacklight/system/flash_message_component.rb +12 -3
  85. data/app/components/blacklight/system/modal_component.html.erb +1 -1
  86. data/app/components/blacklight/system/modal_component.rb +1 -3
  87. data/app/components/blacklight/top_navbar_component.html.erb +1 -1
  88. data/app/components/blacklight/top_navbar_component.rb +0 -4
  89. data/app/controllers/bookmarks_controller.rb +1 -0
  90. data/app/controllers/catalog_controller.rb +1 -0
  91. data/app/controllers/concerns/blacklight/bookmarks.rb +10 -9
  92. data/app/controllers/concerns/blacklight/catalog.rb +21 -83
  93. data/app/controllers/concerns/blacklight/controller.rb +3 -41
  94. data/app/controllers/concerns/blacklight/search_context.rb +25 -7
  95. data/app/controllers/concerns/blacklight/search_history.rb +2 -0
  96. data/app/controllers/concerns/blacklight/searchable.rb +12 -1
  97. data/app/controllers/concerns/blacklight/token_based_user.rb +13 -1
  98. data/app/controllers/search_history_controller.rb +1 -0
  99. data/app/helpers/blacklight/blacklight_helper_behavior.rb +12 -310
  100. data/app/helpers/blacklight/catalog_helper_behavior.rb +22 -139
  101. data/app/helpers/blacklight/component_helper_behavior.rb +2 -53
  102. data/app/helpers/blacklight/configuration_helper_behavior.rb +2 -119
  103. data/app/helpers/blacklight/facets_helper_behavior.rb +4 -321
  104. data/app/helpers/blacklight/icon_helper_behavior.rb +5 -7
  105. data/app/helpers/blacklight/layout_helper_behavior.rb +4 -3
  106. data/app/helpers/blacklight/render_partials_helper_behavior.rb +11 -31
  107. data/app/helpers/blacklight/url_helper_behavior.rb +12 -97
  108. data/app/helpers/blacklight_helper.rb +1 -0
  109. data/app/helpers/catalog_helper.rb +1 -0
  110. data/app/javascript/blacklight/bookmark_toggle.js +13 -19
  111. data/app/javascript/blacklight/button_focus.js +12 -10
  112. data/app/javascript/blacklight/checkbox_submit.js +68 -123
  113. data/app/javascript/blacklight/core.js +5 -7
  114. data/app/javascript/blacklight/index.js +13 -0
  115. data/app/javascript/blacklight/modal.js +99 -164
  116. data/app/javascript/blacklight/modalForm.js +60 -0
  117. data/app/javascript/blacklight/search_context.js +46 -54
  118. data/app/models/blacklight/facet_paginator.rb +3 -2
  119. data/app/models/blacklight/icon.rb +4 -2
  120. data/app/models/bookmark.rb +0 -2
  121. data/app/models/concerns/blacklight/configurable.rb +5 -4
  122. data/app/models/concerns/blacklight/document/active_model_shim.rb +1 -10
  123. data/app/models/concerns/blacklight/document/cache_key.rb +1 -0
  124. data/app/models/concerns/blacklight/document/dublin_core.rb +2 -1
  125. data/app/models/concerns/blacklight/document/email.rb +1 -0
  126. data/app/models/concerns/blacklight/document/export.rb +2 -1
  127. data/app/models/concerns/blacklight/document/extensions.rb +1 -0
  128. data/app/models/concerns/blacklight/document/schema_org.rb +1 -0
  129. data/app/models/concerns/blacklight/document/semantic_fields.rb +2 -1
  130. data/app/models/concerns/blacklight/document/sms.rb +1 -0
  131. data/app/models/concerns/blacklight/suggest/response.rb +1 -0
  132. data/app/models/concerns/blacklight/user.rb +17 -8
  133. data/app/models/record_mailer.rb +13 -12
  134. data/app/models/search.rb +1 -7
  135. data/app/models/solr_document.rb +1 -0
  136. data/app/presenters/blacklight/clause_presenter.rb +1 -1
  137. data/app/presenters/blacklight/document_presenter.rb +23 -50
  138. data/app/presenters/blacklight/facet_field_presenter.rb +39 -14
  139. data/app/presenters/blacklight/facet_grouped_item_presenter.rb +1 -5
  140. data/app/presenters/blacklight/facet_item_pivot_presenter.rb +60 -0
  141. data/app/presenters/blacklight/facet_item_presenter.rb +3 -9
  142. data/app/presenters/blacklight/field_presenter.rb +1 -0
  143. data/app/presenters/blacklight/index_presenter.rb +2 -40
  144. data/app/presenters/blacklight/json_presenter.rb +10 -6
  145. data/app/presenters/blacklight/rendering/link_to_facet.rb +2 -5
  146. data/app/presenters/blacklight/show_presenter.rb +1 -9
  147. data/app/presenters/blacklight/thumbnail_presenter.rb +1 -1
  148. data/app/services/blacklight/bookmarks_search_builder.rb +22 -0
  149. data/app/services/blacklight/field_retriever.rb +12 -21
  150. data/app/services/blacklight/search_service.rb +10 -17
  151. data/app/values/blacklight/types.rb +0 -18
  152. data/app/views/bookmarks/_clear_bookmarks_widget.html.erb +8 -1
  153. data/app/views/bookmarks/_tools.html.erb +7 -12
  154. data/app/views/catalog/_advanced_search_form.html.erb +0 -1
  155. data/app/views/catalog/_bookmark_control.html.erb +1 -1
  156. data/app/views/catalog/_citation.html.erb +1 -1
  157. data/app/views/catalog/_constraints.html.erb +1 -14
  158. data/app/views/catalog/_document.atom.builder +12 -14
  159. data/app/views/catalog/_document.html.erb +5 -3
  160. data/app/views/catalog/_document.rss.builder +2 -4
  161. data/app/views/catalog/_facet_layout.html.erb +2 -2
  162. data/app/views/catalog/_facets.html.erb +5 -4
  163. data/app/views/catalog/_home_text.html.erb +2 -14
  164. data/app/views/catalog/_per_page_widget.html.erb +10 -1
  165. data/app/views/catalog/_search_form.html.erb +2 -2
  166. data/app/views/catalog/_search_header.html.erb +1 -2
  167. data/app/views/catalog/_search_results.html.erb +2 -2
  168. data/app/views/catalog/_search_sidebar.html.erb +5 -1
  169. data/app/views/catalog/_show_main_content.html.erb +11 -16
  170. data/app/views/catalog/_show_sidebar.html.erb +2 -2
  171. data/app/views/catalog/_show_tools.html.erb +8 -14
  172. data/app/views/catalog/_view_type_group.html.erb +1 -1
  173. data/app/views/catalog/email.html.erb +2 -2
  174. data/app/views/catalog/email_success.html.erb +5 -6
  175. data/app/views/catalog/facet.html.erb +7 -5
  176. data/app/views/catalog/index.atom.builder +12 -14
  177. data/app/views/catalog/index.html.erb +4 -1
  178. data/app/views/catalog/index.json.jbuilder +19 -19
  179. data/app/views/catalog/index.rss.builder +1 -1
  180. data/app/views/catalog/opensearch.xml.builder +1 -1
  181. data/app/views/catalog/sms.html.erb +2 -2
  182. data/app/views/catalog/sms_success.html.erb +5 -6
  183. data/app/views/catalog/suggest.html.erb +3 -0
  184. data/app/views/kaminari/blacklight/_page.html.erb +2 -1
  185. data/app/views/layouts/blacklight/base.html.erb +13 -2
  186. data/app/views/search_history/index.html.erb +6 -2
  187. data/app/views/shared/_flash_messages.html.erb +1 -1
  188. data/app/views/shared/_modal.html.erb +3 -3
  189. data/blacklight.gemspec +7 -11
  190. data/config/importmap.rb +3 -0
  191. data/config/locales/blacklight.ar.yml +0 -1
  192. data/config/locales/blacklight.ca.yml +0 -1
  193. data/config/locales/blacklight.de.yml +0 -1
  194. data/config/locales/blacklight.en.yml +0 -2
  195. data/config/locales/blacklight.es.yml +0 -1
  196. data/config/locales/blacklight.fr.yml +0 -1
  197. data/config/locales/blacklight.hu.yml +0 -1
  198. data/config/locales/blacklight.it.yml +0 -1
  199. data/config/locales/blacklight.nl.yml +0 -1
  200. data/config/locales/blacklight.pt-BR.yml +0 -1
  201. data/config/locales/blacklight.sq.yml +0 -1
  202. data/config/locales/blacklight.zh.yml +0 -1
  203. data/config/routes.rb +3 -2
  204. data/db/migrate/20140202020201_create_searches.rb +1 -0
  205. data/db/migrate/20140202020202_create_bookmarks.rb +1 -0
  206. data/db/migrate/20140320000000_add_polymorphic_type_to_bookmarks.rb +1 -0
  207. data/docker-compose.yml +3 -3
  208. data/lib/blacklight/abstract_repository.rb +1 -6
  209. data/lib/blacklight/component.rb +47 -10
  210. data/lib/blacklight/configuration/context.rb +4 -4
  211. data/lib/blacklight/configuration/display_field.rb +7 -9
  212. data/lib/blacklight/configuration/facet_field.rb +17 -11
  213. data/lib/blacklight/configuration/field.rb +1 -0
  214. data/lib/blacklight/configuration/fields.rb +12 -15
  215. data/lib/blacklight/configuration/index_field.rb +1 -0
  216. data/lib/blacklight/configuration/null_display_field.rb +17 -0
  217. data/lib/blacklight/configuration/search_field.rb +1 -0
  218. data/lib/blacklight/configuration/show_field.rb +1 -0
  219. data/lib/blacklight/configuration/sort_field.rb +1 -0
  220. data/lib/blacklight/configuration/tool_config.rb +1 -0
  221. data/lib/blacklight/configuration/view_config.rb +14 -10
  222. data/lib/blacklight/configuration.rb +310 -365
  223. data/lib/blacklight/engine.rb +8 -24
  224. data/lib/blacklight/exceptions.rb +2 -2
  225. data/lib/blacklight/nested_open_struct_with_hash_access.rb +7 -13
  226. data/lib/blacklight/open_struct_with_hash_access.rb +23 -6
  227. data/lib/blacklight/parameters.rb +7 -21
  228. data/lib/blacklight/routes/exportable.rb +1 -0
  229. data/lib/blacklight/routes/searchable.rb +2 -1
  230. data/lib/blacklight/routes.rb +1 -0
  231. data/lib/blacklight/search_builder.rb +10 -10
  232. data/lib/blacklight/search_state/filter_field.rb +8 -25
  233. data/lib/blacklight/search_state/pivot_filter_field.rb +144 -0
  234. data/lib/blacklight/search_state.rb +23 -79
  235. data/lib/blacklight/solr/document.rb +1 -0
  236. data/lib/blacklight/solr/facet_paginator.rb +1 -0
  237. data/lib/blacklight/solr/repository.rb +4 -24
  238. data/lib/blacklight/solr/request.rb +1 -0
  239. data/lib/blacklight/solr/response/facets.rb +21 -5
  240. data/lib/blacklight/solr/response/group.rb +1 -0
  241. data/lib/blacklight/solr/response/group_response.rb +1 -0
  242. data/lib/blacklight/solr/response/more_like_this.rb +1 -0
  243. data/lib/blacklight/solr/response/pagination_methods.rb +4 -3
  244. data/lib/blacklight/solr/response/params.rb +5 -4
  245. data/lib/blacklight/solr/response/response.rb +1 -0
  246. data/lib/blacklight/solr/response/spelling.rb +1 -0
  247. data/lib/blacklight/solr/response.rb +16 -3
  248. data/lib/blacklight/solr/search_builder_behavior.rb +16 -35
  249. data/lib/blacklight/solr.rb +7 -0
  250. data/lib/blacklight/version.rb +1 -0
  251. data/lib/blacklight.rb +26 -14
  252. data/lib/generators/blacklight/assets/importmap_generator.rb +55 -0
  253. data/lib/generators/blacklight/assets/propshaft_generator.rb +25 -0
  254. data/lib/generators/blacklight/assets/sprockets_generator.rb +66 -0
  255. data/lib/generators/blacklight/assets_generator.rb +13 -86
  256. data/lib/generators/blacklight/controller_generator.rb +4 -3
  257. data/lib/generators/blacklight/document_generator.rb +1 -0
  258. data/lib/generators/blacklight/install_generator.rb +4 -3
  259. data/lib/generators/blacklight/models_generator.rb +1 -0
  260. data/lib/generators/blacklight/search_builder_generator.rb +1 -0
  261. data/lib/generators/blacklight/solr_generator.rb +1 -1
  262. data/lib/generators/blacklight/templates/catalog_controller.rb +34 -8
  263. data/lib/generators/blacklight/templates/solr/conf/solrconfig.xml +1 -70
  264. data/lib/generators/blacklight/test_support_generator.rb +5 -3
  265. data/lib/generators/blacklight/user_generator.rb +7 -9
  266. data/lib/railties/blacklight.rake +6 -7
  267. data/package.json +10 -13
  268. data/rollup.config.js +27 -0
  269. data/spec/components/blacklight/constraints_component_spec.rb +17 -13
  270. data/spec/components/blacklight/document/action_component_spec.rb +6 -1
  271. data/spec/components/blacklight/document_component_spec.rb +22 -131
  272. data/spec/components/blacklight/facet_component_spec.rb +3 -18
  273. data/spec/components/blacklight/facet_field_checkboxes_component_spec.rb +1 -2
  274. data/spec/components/blacklight/facet_field_list_component_spec.rb +7 -6
  275. data/spec/components/blacklight/facet_item_pivot_component_spec.rb +10 -9
  276. data/spec/components/blacklight/response/view_type_component_spec.rb +66 -0
  277. data/spec/components/blacklight/search_bar_component_spec.rb +1 -1
  278. data/spec/components/blacklight/search_context_component_spec.rb +17 -8
  279. data/spec/controllers/blacklight/catalog/component_configuration_spec.rb +1 -6
  280. data/spec/controllers/blacklight/{base_spec.rb → catalog_spec.rb} +2 -2
  281. data/spec/controllers/bookmarks_controller_spec.rb +2 -3
  282. data/spec/controllers/catalog_controller_spec.rb +13 -135
  283. data/spec/features/advanced_search_spec.rb +0 -56
  284. data/spec/features/autocomplete_spec.rb +1 -1
  285. data/spec/features/axe_spec.rb +1 -6
  286. data/spec/features/bookmarks_spec.rb +1 -1
  287. data/spec/features/facets_spec.rb +6 -4
  288. data/spec/features/search_context_spec.rb +5 -11
  289. data/spec/features/search_results_spec.rb +0 -33
  290. data/spec/features/sitelinks_search_box.rb +13 -0
  291. data/spec/helpers/blacklight/configuration_helper_behavior_spec.rb +2 -138
  292. data/spec/helpers/blacklight/facets_helper_behavior_spec.rb +0 -387
  293. data/spec/helpers/blacklight/icon_helper_behavior_spec.rb +8 -0
  294. data/spec/helpers/blacklight/layout_helper_behavior_spec.rb +3 -20
  295. data/spec/helpers/blacklight/render_partials_helper_behavior_spec.rb +5 -7
  296. data/spec/helpers/blacklight/url_helper_behavior_spec.rb +9 -131
  297. data/spec/helpers/blacklight_helper_spec.rb +8 -252
  298. data/spec/helpers/catalog_helper_spec.rb +7 -118
  299. data/spec/i18n_spec.rb +1 -0
  300. data/spec/integration/generators/blacklight/solr_generator_spec.rb +1 -1
  301. data/spec/lib/blacklight/component_spec.rb +27 -32
  302. data/spec/lib/blacklight/configuration/facet_field_spec.rb +27 -16
  303. data/spec/lib/blacklight/configuration/field_spec.rb +1 -1
  304. data/spec/lib/blacklight/configuration/view_config_spec.rb +1 -1
  305. data/spec/lib/blacklight/open_struct_with_hash_access_spec.rb +2 -2
  306. data/spec/lib/blacklight/parameters_spec.rb +1 -4
  307. data/spec/lib/blacklight/search_state/filter_field_spec.rb +4 -4
  308. data/spec/lib/blacklight/search_state/pivot_filter_field_spec.rb +117 -0
  309. data/spec/lib/blacklight/search_state_spec.rb +80 -198
  310. data/spec/lib/tasks/blacklight_task_spec.rb +1 -0
  311. data/spec/models/blacklight/configuration_spec.rb +17 -51
  312. data/spec/models/blacklight/document/active_model_shim_spec.rb +2 -2
  313. data/spec/models/blacklight/icon_spec.rb +31 -15
  314. data/spec/models/blacklight/search_builder_spec.rb +9 -9
  315. data/spec/models/blacklight/solr/document_spec.rb +3 -3
  316. data/spec/models/blacklight/solr/repository_spec.rb +0 -45
  317. data/spec/models/blacklight/solr/response/facets_spec.rb +27 -27
  318. data/spec/models/blacklight/solr/response/group_response_spec.rb +1 -0
  319. data/spec/models/blacklight/solr/response/group_spec.rb +1 -0
  320. data/spec/models/blacklight/solr/response_spec.rb +9 -2
  321. data/spec/models/blacklight/solr/search_builder_spec.rb +24 -44
  322. data/spec/models/blacklight/user_spec.rb +22 -0
  323. data/spec/models/solr_document_spec.rb +3 -9
  324. data/spec/presenters/blacklight/clause_presenter_spec.rb +1 -0
  325. data/spec/presenters/blacklight/document_presenter_spec.rb +2 -3
  326. data/spec/presenters/blacklight/facet_field_presenter_spec.rb +85 -12
  327. data/spec/presenters/blacklight/facet_grouped_item_presenter_spec.rb +1 -0
  328. data/spec/presenters/blacklight/facet_item_presenter_spec.rb +14 -13
  329. data/spec/presenters/blacklight/field_presenter_spec.rb +0 -14
  330. data/spec/presenters/blacklight/index_presenter_spec.rb +2 -5
  331. data/spec/presenters/blacklight/json_presenter_spec.rb +1 -0
  332. data/spec/presenters/blacklight/link_alternate_presenter_spec.rb +3 -2
  333. data/spec/presenters/blacklight/show_presenter_spec.rb +20 -30
  334. data/spec/presenters/thumbnail_presenter_spec.rb +1 -1
  335. data/spec/requests/load_suggestions_spec.rb +16 -0
  336. data/spec/routing/catalog_routing_spec.rb +2 -1
  337. data/spec/services/blacklight/search_service_spec.rb +39 -76
  338. data/spec/spec_helper.rb +8 -9
  339. data/spec/support/controller_level_helpers.rb +1 -2
  340. data/spec/support/features/search_helpers.rb +39 -0
  341. data/spec/support/features/session_helpers.rb +1 -0
  342. data/spec/support/features.rb +3 -0
  343. data/spec/support/view_component_capybara_test_helpers.rb +8 -0
  344. data/spec/test_app_templates/Gemfile.extra +1 -0
  345. data/spec/test_app_templates/lib/generators/test_app_generator.rb +9 -2
  346. data/spec/views/catalog/_document.html.erb_spec.rb +3 -34
  347. data/spec/views/catalog/_facet_index_navigation.html.erb_spec.rb +1 -1
  348. data/spec/views/catalog/_paginate_compact.html.erb_spec.rb +0 -2
  349. data/spec/views/catalog/_search_header.erb_spec.rb +1 -0
  350. data/spec/views/catalog/_show_sidebar.erb_spec.rb +1 -0
  351. data/spec/views/catalog/_show_tools.html.erb_spec.rb +5 -66
  352. data/spec/views/catalog/_view_type_group.html.erb_spec.rb +17 -9
  353. data/spec/views/catalog/email_success.html.erb_spec.rb +2 -2
  354. data/spec/views/catalog/facet.html.erb_spec.rb +6 -3
  355. data/spec/views/catalog/index.atom.builder_spec.rb +17 -11
  356. data/spec/views/catalog/index.html.erb_spec.rb +5 -6
  357. data/spec/views/catalog/index.json.jbuilder_spec.rb +2 -2
  358. data/spec/views/catalog/show.html.erb_spec.rb +3 -25
  359. data/spec/views/catalog/sms_success.html.erb_spec.rb +2 -2
  360. data/tasks/blacklight.rake +11 -9
  361. data/template.demo.rb +7 -7
  362. metadata +77 -189
  363. data/.babelrc +0 -11
  364. data/.github/matrix.json +0 -62
  365. data/.github/workflows/build.yml +0 -16
  366. data/.github/workflows/lint.yml +0 -23
  367. data/.github/workflows/main.yml +0 -23
  368. data/.github/workflows/test.yml +0 -53
  369. data/app/assets/images/blacklight/list.svg +0 -1
  370. data/app/assets/images/blacklight/search.svg +0 -1
  371. data/app/assets/stylesheets/blacklight/_twitter_typeahead.scss +0 -37
  372. data/app/components/blacklight/content_areas_shim.rb +0 -13
  373. data/app/components/blacklight/search/per_page_component.html.erb +0 -2
  374. data/app/components/blacklight/search/per_page_component.rb +0 -50
  375. data/app/components/blacklight/search_context/server_item_pagination_component.html.erb +0 -10
  376. data/app/components/blacklight/search_context/server_item_pagination_component.rb +0 -15
  377. data/app/components/blacklight/system/dropdown_button_component.rb +0 -18
  378. data/app/controllers/concerns/blacklight/base.rb +0 -12
  379. data/app/controllers/concerns/blacklight/default_component_configuration.rb +0 -64
  380. data/app/controllers/concerns/blacklight/facet.rb +0 -69
  381. data/app/controllers/concerns/blacklight/search_fields.rb +0 -46
  382. data/app/helpers/blacklight/hash_as_hidden_fields_helper_behavior.rb +0 -27
  383. data/app/helpers/blacklight/render_constraints_helper_behavior.rb +0 -188
  384. data/app/helpers/blacklight/search_history_constraints_helper_behavior.rb +0 -97
  385. data/app/helpers/blacklight/suggest_helper_behavior.rb +0 -13
  386. data/app/javascript/blacklight/autocomplete.js +0 -36
  387. data/app/javascript/blacklight/facet_load.js +0 -22
  388. data/app/presenters/blacklight/search_bar_presenter.rb +0 -47
  389. data/app/views/catalog/_constraints_element.html.erb +0 -14
  390. data/app/views/catalog/_document_action.html.erb +0 -5
  391. data/app/views/catalog/_facet_group.html.erb +0 -5
  392. data/app/views/catalog/_facet_limit.html.erb +0 -3
  393. data/app/views/catalog/_index.html.erb +0 -1
  394. data/app/views/catalog/_index_header.html.erb +0 -22
  395. data/app/views/catalog/_previous_next_doc.html.erb +0 -2
  396. data/app/views/catalog/_show.html.erb +0 -6
  397. data/app/views/catalog/_show_header.html.erb +0 -2
  398. data/app/views/catalog/_thumbnail.html.erb +0 -1
  399. data/lib/blacklight/deprecations/engine_configuration.rb +0 -66
  400. data/lib/blacklight/deprecations/search_state_normalization.rb +0 -52
  401. data/spec/components/blacklight/header_component_spec.rb +0 -20
  402. data/spec/components/blacklight/icons/icon_component_spec.rb +0 -42
  403. data/spec/components/blacklight/response/pagination_component_spec.rb +0 -53
  404. data/spec/components/blacklight/search_context/server_item_pagination_component_spec.rb +0 -35
  405. data/spec/controllers/blacklight/facet_spec.rb +0 -33
  406. data/spec/controllers/blacklight/search_fields_spec.rb +0 -62
  407. data/spec/features/citation_spec.rb +0 -10
  408. data/spec/features/sitelinks_search_box_spec.rb +0 -13
  409. data/spec/features/sms_spec.rb +0 -12
  410. data/spec/helpers/blacklight/hash_as_hidden_fields_behavior_spec.rb +0 -26
  411. data/spec/helpers/blacklight/render_constraints_helper_behavior_spec.rb +0 -92
  412. data/spec/helpers/blacklight/search_history_constraints_helper_behavior_spec.rb +0 -101
  413. data/spec/helpers/blacklight/suggest_helper_behavior_spec.rb +0 -48
  414. data/spec/lib/blacklight/engine_spec.rb +0 -41
  415. data/spec/presenters/blacklight/search_bar_presenter_spec.rb +0 -94
  416. data/spec/services/blacklight/field_retriever_spec.rb +0 -17
  417. data/spec/support/view_component_test_helpers.rb +0 -35
  418. data/spec/views/catalog/_constraints.html.erb_spec.rb +0 -33
  419. data/spec/views/catalog/_facet_group.html.erb_spec.rb +0 -84
  420. data/spec/views/catalog/_facets.html.erb_spec.rb +0 -15
  421. data/spec/views/catalog/_index.html.erb_spec.rb +0 -62
  422. data/spec/views/catalog/_index_header.html.erb_spec.rb +0 -35
  423. data/spec/views/catalog/_previous_next_doc.html.erb_spec.rb +0 -22
  424. data/spec/views/catalog/_show.html.erb_spec.rb +0 -62
  425. data/spec/views/catalog/_thumbnail.html.erb_spec.rb +0 -38
@@ -1,511 +1,392 @@
1
- "use strict";
2
-
3
- const Blacklight = function () {
4
- var buffer = new Array();
5
- return {
6
- onLoad: function (func) {
7
- buffer.push(func);
8
- },
9
- activate: function () {
10
- for (var i = 0; i < buffer.length; i++) {
11
- buffer[i].call();
12
- }
13
- },
14
- listeners: function () {
15
- var listeners = [];
16
- if (typeof Turbo !== 'undefined') {
17
- listeners.push('turbo:load');
18
- } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
19
- // Turbolinks 5
20
- if (Turbolinks.BrowserAdapter) {
21
- listeners.push('turbolinks:load');
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
+ const Blacklight = function() {
8
+ const buffer = new Array;
9
+ return {
10
+ onLoad: function(func) {
11
+ buffer.push(func);
12
+ },
13
+
14
+ activate: function() {
15
+ for(let i = 0; i < buffer.length; i++) {
16
+ buffer[i].call();
17
+ }
18
+ },
19
+
20
+ listeners: function () {
21
+ const listeners = [];
22
+ if (typeof Turbo !== 'undefined') {
23
+ listeners.push('turbo:load', 'turbo:frame-load');
24
+ } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
25
+ // Turbolinks 5
26
+ if (Turbolinks.BrowserAdapter) {
27
+ listeners.push('turbolinks:load');
28
+ } else {
29
+ // Turbolinks < 5
30
+ listeners.push('page:load', 'DOMContentLoaded');
31
+ }
22
32
  } else {
23
- // Turbolinks < 5
24
- listeners.push('page:load', 'DOMContentLoaded');
33
+ listeners.push('DOMContentLoaded');
25
34
  }
26
- } else {
27
- listeners.push('DOMContentLoaded');
28
- }
29
- return listeners;
30
- }
31
- };
32
- }();
33
35
 
34
- // turbolinks triggers page:load events on page transition
35
- // If app isn't using turbolinks, this event will never be triggered, no prob.
36
- Blacklight.listeners().forEach(function (listener) {
37
- document.addEventListener(listener, function () {
38
- Blacklight.activate();
39
- });
40
- });
41
- Blacklight.onLoad(function () {
42
- const elem = document.querySelector('.no-js');
43
-
44
- // The "no-js" class may already have been removed because this function is
45
- // run on every turbo:load event, in that case, it won't find an element.
46
- if (!elem) return;
47
- elem.classList.remove('no-js');
48
- elem.classList.add('js');
49
- });
50
- Blacklight.csrfToken = () => {
51
- var _document$querySelect;
52
- return (_document$querySelect = document.querySelector('meta[name=csrf-token]')) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.content;
53
- };
54
- Blacklight.csrfParam = () => {
55
- var _document$querySelect2;
56
- return (_document$querySelect2 = document.querySelector('meta[name=csrf-param]')) === null || _document$querySelect2 === void 0 ? void 0 : _document$querySelect2.content;
57
- };
58
- window.Blacklight = Blacklight;
59
- /*global Bloodhound */
60
-
61
- Blacklight.onLoad(function () {
62
- 'use strict';
63
-
64
- $('[data-autocomplete-enabled="true"]').each(function () {
65
- var $el = $(this);
66
- if ($el.hasClass('tt-hint')) {
67
- return;
68
- }
69
- var suggestUrl = $el.data().autocompletePath;
70
- var terms = new Bloodhound({
71
- datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
72
- queryTokenizer: Bloodhound.tokenizers.whitespace,
73
- remote: {
74
- url: suggestUrl + '?q=%QUERY',
75
- wildcard: '%QUERY'
36
+ return listeners;
76
37
  }
77
- });
78
- terms.initialize();
79
- $el.typeahead({
80
- hint: true,
81
- highlight: true,
82
- minLength: 2
83
- }, {
84
- name: 'terms',
85
- displayKey: 'term',
86
- source: terms.ttAdapter()
38
+ };
39
+ }();
40
+
41
+ // turbolinks triggers page:load events on page transition
42
+ // If app isn't using turbolinks, this event will never be triggered, no prob.
43
+ Blacklight.listeners().forEach(function(listener) {
44
+ document.addEventListener(listener, function() {
45
+ Blacklight.activate();
87
46
  });
88
47
  });
89
- });
90
- (function ($) {
91
- //change form submit toggle to checkbox
92
- Blacklight.doBookmarkToggleBehavior = function () {
93
- if (typeof Blacklight.do_bookmark_toggle_behavior == 'function') {
94
- console.warn("do_bookmark_toggle_behavior is deprecated. Use doBookmarkToggleBehavior instead.");
95
- return Blacklight.do_bookmark_toggle_behavior();
96
- }
97
- $(Blacklight.doBookmarkToggleBehavior.selector).blCheckboxSubmit({
98
- // cssClass is added to elements added, plus used for id base
99
- cssClass: 'toggle-bookmark',
100
- success: function (checked, response) {
101
- if (response.bookmarks) {
102
- $('[data-role=bookmark-counter]').text(response.bookmarks.count);
103
- }
104
- }
105
- });
106
- };
107
- Blacklight.doBookmarkToggleBehavior.selector = 'form.bookmark-toggle';
48
+
108
49
  Blacklight.onLoad(function () {
109
- Blacklight.doBookmarkToggleBehavior();
110
- });
111
- })(jQuery);
112
- Blacklight.onLoad(function () {
113
- // Button clicks should change focus. As of 10/3/19, Firefox for Mac and
114
- // Safari both do not set focus to a button on button click.
115
- // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information
116
- document.querySelectorAll('button.collapse-toggle').forEach(button => {
117
- button.addEventListener('click', () => {
118
- event.target.focus();
119
- });
50
+ const elem = document.querySelector('.no-js');
51
+
52
+ // The "no-js" class may already have been removed because this function is
53
+ // run on every turbo:load event, in that case, it won't find an element.
54
+ if (!elem) return;
55
+
56
+ elem.classList.remove('no-js');
57
+ elem.classList.add('js');
120
58
  });
121
- });
122
- /* A JQuery plugin (should this be implemented as a widget instead? not sure)
123
- that will convert a "toggle" form, with single submit button to add/remove
124
- something, like used for Bookmarks, into an AJAXy checkbox instead.
125
-
126
- Apply to a form. Does require certain assumption about the form:
127
- 1) The same form 'action' href must be used for both ADD and REMOVE
128
- actions, with the different being the hidden input name="_method"
129
- being set to "put" or "delete" -- that's the Rails method to pretend
130
- to be doing a certain HTTP verb. So same URL, PUT to add, DELETE
131
- to remove. This plugin assumes that.
132
-
133
- Plus, the form this is applied to should provide a data-doc-id
134
- attribute (HTML5-style doc-*) that contains the id/primary key
135
- of the object in question -- used by plugin for a unique value for
136
- DOM id's.
137
-
138
- Uses HTML for a checkbox compatible with Bootstrap 3.
139
-
140
- Pass in options for your class name and labels:
141
- $("form.something").blCheckboxSubmit({
142
- //cssClass is added to elements added, plus used for id base
143
- cssClass: "toggle_my_kinda_form",
144
- error: function() {
145
- #optional callback
146
- },
147
- success: function(after_success_check_state) {
148
- #optional callback
149
- }
150
- });
151
- */
152
- (function ($) {
153
- $.fn.blCheckboxSubmit = function (argOpts) {
154
- this.each(function () {
155
- var options = $.extend({}, $.fn.blCheckboxSubmit.defaults, argOpts);
156
- var form = $(this);
157
- form.children().hide();
158
- //We're going to use the existing form to actually send our add/removes
159
- //This works conveneintly because the exact same action href is used
160
- //for both bookmarks/$doc_id. But let's take out the irrelevant parts
161
- //of the form to avoid any future confusion.
162
- form.find('input[type=submit]').remove();
163
-
164
- //View needs to set data-doc-id so we know a unique value
165
- //for making DOM id
166
- var uniqueId = form.attr('data-doc-id') || Math.random();
167
- // if form is currently using method delete to change state,
168
- // then checkbox is currently checked
169
- var checked = form.find('input[name=_method][value=delete]').length != 0;
170
- var checkbox = $('<input type="checkbox">').addClass(options.cssClass).attr('id', options.cssClass + '_' + uniqueId);
171
- var label = $('<label>').addClass(options.cssClass).attr('for', options.cssClass + '_' + uniqueId).attr('title', form.attr('title') || '');
172
- var span = $('<span>');
173
- label.append(checkbox);
174
- label.append(' ');
175
- label.append(span);
176
- var checkboxDiv = $('<div class="checkbox" />').addClass(options.cssClass).append(label);
177
- function updateStateFor(state) {
178
- checkbox.prop('checked', state);
179
- label.toggleClass('checked', state);
180
- if (state) {
181
- //Set the Rails hidden field that fakes an HTTP verb
182
- //properly for current state action.
183
- form.find('input[name=_method]').val('delete');
184
- span.html(form.attr('data-present'));
185
- } else {
186
- form.find('input[name=_method]').val('put');
187
- span.html(form.attr('data-absent'));
59
+
60
+ /* Converts a "toggle" form, with single submit button to add/remove
61
+ something, like used for Bookmarks, into an AJAXy checkbox instead.
62
+ Apply to a form. Does require certain assumption about the form:
63
+ 1) The same form 'action' href must be used for both ADD and REMOVE
64
+ actions, with the different being the hidden input name="_method"
65
+ being set to "put" or "delete" -- that's the Rails method to pretend
66
+ to be doing a certain HTTP verb. So same URL, PUT to add, DELETE
67
+ to remove. This plugin assumes that.
68
+ Plus, the form this is applied to should provide a data-doc-id
69
+ attribute (HTML5-style doc-*) that contains the id/primary key
70
+ of the object in question -- used by plugin for a unique value for
71
+ DOM id's.
72
+ Uses HTML for a checkbox compatible with Bootstrap 4.
73
+ new CheckboxSubmit(document.querySelector('form.something')).render()
74
+ */
75
+ class CheckboxSubmit {
76
+ constructor(form) {
77
+ this.form = form;
78
+ }
79
+
80
+ async clicked(evt) {
81
+ this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress');
82
+ this.labelTarget.setAttribute('disabled', 'disabled');
83
+ this.checkboxTarget.setAttribute('disabled', 'disabled');
84
+ const response = await fetch(this.formTarget.getAttribute('action'), {
85
+ body: new FormData(this.formTarget),
86
+ method: this.formTarget.getAttribute('method').toUpperCase(),
87
+ headers: {
88
+ 'Accept': 'application/json',
89
+ 'X-Requested-With': 'XMLHttpRequest',
90
+ 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content
188
91
  }
92
+ });
93
+ this.labelTarget.removeAttribute('disabled');
94
+ this.checkboxTarget.removeAttribute('disabled');
95
+ if (response.ok) {
96
+ const json = await response.json();
97
+ this.updateStateFor(!this.checked);
98
+ document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count;
99
+ } else {
100
+ alert('Error');
189
101
  }
190
- form.append(checkboxDiv);
191
- updateStateFor(checked);
192
- checkbox.click(function () {
193
- span.html(form.attr('data-inprogress'));
194
- label.attr('disabled', 'disabled');
195
- checkbox.attr('disabled', 'disabled');
196
- $.ajax({
197
- url: form.attr('action'),
198
- beforeSend: function (xhr) {
199
- xhr.setRequestHeader('X-CSRF-Token', Blacklight.csrfToken());
200
- },
201
- dataType: 'json',
202
- type: form.attr('method').toUpperCase(),
203
- data: form.serialize(),
204
- error: function () {
205
- label.removeAttr('disabled');
206
- checkbox.removeAttr('disabled');
207
- options.error.call();
208
- },
209
- success: function (data, status, xhr) {
210
- //if app isn't running at all, xhr annoyingly
211
- //reports success with status 0.
212
- if (xhr.status != 0) {
213
- checked = !checked;
214
- updateStateFor(checked);
215
- label.removeAttr('disabled');
216
- checkbox.removeAttr('disabled');
217
- options.success.call(form, checked, xhr.responseJSON);
218
- } else {
219
- label.removeAttr('disabled');
220
- checkbox.removeAttr('disabled');
221
- options.error.call();
222
- }
223
- }
224
- });
225
- return false;
226
- }); //checkbox.click
227
- }); //this.each
228
- return this;
229
- };
230
- $.fn.blCheckboxSubmit.defaults = {
231
- //cssClass is added to elements added, plus used for id base
232
- cssClass: 'blCheckboxSubmit',
233
- error: function () {
234
- alert("Error");
235
- },
236
- success: function () {} //callback
237
- };
238
- })(jQuery);
239
- /*global Blacklight */
240
-
241
- 'use strict';
242
- Blacklight.doResizeFacetLabelsAndCounts = function () {
243
- // adjust width of facet columns to fit their contents
244
- function longer(a, b) {
245
- return b.textContent.length - a.textContent.length;
246
- }
247
- document.querySelectorAll('.facet-values, .pivot-facet').forEach(function (elem) {
248
- const nodes = elem.querySelectorAll('.facet-count');
249
- // TODO: when we drop ie11 support, this can become the spread operator:
250
- const longest = Array.from(nodes).sort(longer)[0];
251
- if (longest && longest.textContent) {
252
- const width = longest.textContent.length + 1 + 'ch';
253
- elem.querySelector('.facet-count').style.width = width;
254
102
  }
255
- });
256
- };
257
- Blacklight.onLoad(function () {
258
- Blacklight.doResizeFacetLabelsAndCounts();
259
- });
260
- /*
261
- The blacklight modal plugin can display some interactions inside a Bootstrap
262
- modal window, including some multi-page interactions.
263
-
264
- It supports unobtrusive Javascript, where a link or form that would have caused
265
- a new page load is changed to display it's results inside a modal dialog,
266
- by this plugin. The plugin assumes there is a Bootstrap modal div
267
- on the page with id #blacklight-modal to use as the modal -- the standard Blacklight
268
- layout provides this.
269
-
270
- To make a link or form have their results display inside a modal, add
271
- `data-blacklight-modal="trigger"` to the link or form. (Note, form itself not submit input)
272
- With Rails link_to helper, you'd do that like:
273
-
274
- link_to something, link, data: { blacklight_modal: "trigger" }
275
-
276
- The results of the link href or form submit will be displayed inside
277
- a modal -- they should include the proper HTML markup for a bootstrap modal's
278
- contents. Also, you ordinarily won't want the Rails template with wrapping
279
- navigational elements to be used. The Rails controller could suppress
280
- the layout when a JS AJAX request is detected, OR the response
281
- can include a `<div data-blacklight-modal="container">` -- only the contents
282
- of the container will be placed inside the modal, the rest of the
283
- page will be ignored.
284
-
285
- If you'd like to have a link or button that closes the modal,
286
- you can just add a `data-dismiss="modal"` to the link,
287
- standard Bootstrap convention. But you can also have
288
- an href on this link for non-JS contexts, we'll make sure
289
- inside the modal it closes the modal and the link is NOT followed.
290
-
291
- Link or forms inside the modal will ordinarily cause page loads
292
- when they are triggered. However, if you'd like their results
293
- to stay within the modal, just add `data-blacklight-modal="preserve"`
294
- to the link or form.
295
-
296
- Here's an example of what might be returned, demonstrating most of the devices available:
297
-
298
- <div data-blacklight-modal="container">
299
- <div class="modal-header">
300
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
301
- <h3 class="modal-title">Request Placed</h3>
302
- </div>
303
103
 
304
- <div class="modal-body">
305
- <p>Some message</p>
306
- <%= link_to "This result will still be within modal", some_link, data: { blacklight_modal: "preserve" } %>
307
- </div>
104
+ get checked() {
105
+ return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)
106
+ }
308
107
 
108
+ get formTarget() {
109
+ return this.form
110
+ }
309
111
 
310
- <div class="modal-footer">
311
- <%= link_to "Close the modal", request_done_path, class: "submit button dialog-close", data: { dismiss: "modal" } %>
312
- </div>
313
- </div>
314
-
315
-
316
- One additional feature. If the content returned from the AJAX modal load
317
- has an element with `data-blacklight-modal=close`, that will trigger the modal
318
- to be closed. And if this element includes a node with class "flash_messages",
319
- the flash-messages node will be added to the main page inside #main-flahses.
320
-
321
- == Events
322
-
323
- We'll send out an event 'loaded.blacklight.blacklight-modal' with the #blacklight-modal
324
- dialog as the target, right after content is loaded into the modal but before
325
- it is shown (if not already a shown modal). In an event handler, you can
326
- inspect loaded content by looking inside $(this). If you call event.preventDefault(),
327
- we won't 'show' the dialog (although it may already have been shown, you may want to
328
- $(this).modal("hide") if you want to ensure hidden/closed.
329
-
330
- The data-blacklight-modal=close behavior is implemented with this event, see for example.
331
- */
332
-
333
- // We keep all our data in Blacklight.modal object.
334
- // Create lazily if someone else created first.
335
- if (Blacklight.modal === undefined) {
336
- Blacklight.modal = {};
337
- }
338
-
339
- // a Bootstrap modal div that should be already on the page hidden
340
- Blacklight.modal.modalSelector = '#blacklight-modal';
341
-
342
- // Trigger selectors identify forms or hyperlinks that should open
343
- // inside a modal dialog.
344
- Blacklight.modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';
345
- Blacklight.modal.triggerFormSelector = 'form[data-blacklight-modal~=trigger]';
346
-
347
- // preserve selectors identify forms or hyperlinks that, if activated already
348
- // inside a modal dialog, should have destinations remain inside the modal -- but
349
- // won't trigger a modal if not already in one.
350
- //
351
- // No need to repeat selectors from trigger selectors, those will already
352
- // be preserved. MUST be manually prefixed with the modal selector,
353
- // so they only apply to things inside a modal.
354
- Blacklight.modal.preserveLinkSelector = Blacklight.modal.modalSelector + ' a[data-blacklight-modal~=preserve]';
355
- Blacklight.modal.containerSelector = '[data-blacklight-modal~=container]';
356
- Blacklight.modal.modalCloseSelector = '[data-blacklight-modal~=close]';
357
-
358
- // Called on fatal failure of ajax load, function returns content
359
- // to show to user in modal. Right now called only for extreme
360
- // network errors.
361
- Blacklight.modal.onFailure = function (jqXHR, textStatus, errorThrown) {
362
- console.error('Server error:', this.url, jqXHR.status, errorThrown);
363
- var contents = '<div class="modal-header">' + '<div class="modal-title">There was a problem with your request.</div>' + '<button type="button" class="blacklight-modal-close btn-close close" data-dismiss="modal" aria-label="Close">' + ' <span aria-hidden="true">&times;</span>' + '</button></div>' + ' <div class="modal-body"><p>Expected a successful response from the server, but got an error</p>' + '<pre>' + this.type + ' ' + this.url + "\n" + jqXHR.status + ': ' + errorThrown + '</pre></div>';
364
- $(Blacklight.modal.modalSelector).find('.modal-content').html(contents);
365
- Blacklight.modal.show();
366
- };
367
- Blacklight.modal.receiveAjax = function (contents) {
368
- // does it have a data- selector for container?
369
- // important we don't execute script tags, we shouldn't.
370
- // code modelled off of JQuery ajax.load. https://github.com/jquery/jquery/blob/main/src/ajax/load.js?source=c#L62
371
- var container = $('<div>').append(jQuery.parseHTML(contents)).find(Blacklight.modal.containerSelector).first();
372
- if (container.length !== 0) {
373
- contents = container.html();
374
- }
375
- $(Blacklight.modal.modalSelector).find('.modal-content').html(contents);
376
-
377
- // send custom event with the modal dialog div as the target
378
- var e = $.Event('loaded.blacklight.blacklight-modal');
379
- $(Blacklight.modal.modalSelector).trigger(e);
380
- // if they did preventDefault, don't show the dialog
381
- if (e.isDefaultPrevented()) return;
382
- Blacklight.modal.show();
383
- };
384
- Blacklight.modal.modalAjaxLinkClick = function (e) {
385
- e.preventDefault();
386
- $.ajax({
387
- url: $(this).attr('href')
388
- }).fail(Blacklight.modal.onFailure).done(Blacklight.modal.receiveAjax);
389
- };
390
- Blacklight.modal.modalAjaxFormSubmit = function (e) {
391
- e.preventDefault();
392
- $.ajax({
393
- url: $(this).attr('action'),
394
- data: $(this).serialize(),
395
- type: $(this).attr('method') // POST
396
- }).fail(Blacklight.modal.onFailure).done(Blacklight.modal.receiveAjax);
397
- };
398
- Blacklight.modal.setupModal = function () {
399
- // Event indicating blacklight is setting up a modal link,
400
- // you can catch it and call e.preventDefault() to abort
401
- // setup.
402
- var e = $.Event('setup.blacklight.blacklight-modal');
403
- $('body').trigger(e);
404
- if (e.isDefaultPrevented()) return;
405
-
406
- // Register both trigger and preserve selectors in ONE event handler, combining
407
- // into one selector with a comma, so if something matches BOTH selectors, it
408
- // still only gets the event handler called once.
409
- $('body').on('click', Blacklight.modal.triggerLinkSelector + ', ' + Blacklight.modal.preserveLinkSelector, Blacklight.modal.modalAjaxLinkClick);
410
- $('body').on('submit', Blacklight.modal.triggerFormSelector + ', ' + Blacklight.modal.preserveFormSelector, Blacklight.modal.modalAjaxFormSubmit);
411
-
412
- // Catch our own custom loaded event to implement data-blacklight-modal=closed
413
- $('body').on('loaded.blacklight.blacklight-modal', Blacklight.modal.checkCloseModal);
414
-
415
- // we support doing data-dismiss=modal on a <a> with a href for non-ajax
416
- // use, we need to suppress following the a's href that's there for
417
- // non-JS contexts.
418
- $('body').on('click', Blacklight.modal.modalSelector + ' a[data-dismiss~=modal]', function (e) {
419
- e.preventDefault();
420
- });
421
- };
422
-
423
- // A function used as an event handler on loaded.blacklight.blacklight-modal
424
- // to catch contained data-blacklight-modal=closed directions
425
- Blacklight.modal.checkCloseModal = function (event) {
426
- if ($(event.target).find(Blacklight.modal.modalCloseSelector).length) {
427
- var modalFlashes = $(this).find('.flash_messages');
428
- Blacklight.modal.hide(event.target);
429
- event.preventDefault();
430
- var mainFlashes = $('#main-flashes');
431
- mainFlashes.append(modalFlashes);
432
- modalFlashes.fadeIn(500);
433
- }
434
- };
435
- Blacklight.modal.hide = function (el) {
436
- if (typeof bootstrap !== 'undefined' && typeof bootstrap.Modal !== 'undefined' && bootstrap.Modal.VERSION >= "5") {
437
- bootstrap.Modal.getOrCreateInstance(el || document.querySelector(Blacklight.modal.modalSelector)).hide();
438
- } else {
439
- $(el || Blacklight.modal.modalSelector).modal('hide');
440
- }
441
- };
442
- Blacklight.modal.show = function (el) {
443
- if (typeof bootstrap !== 'undefined' && typeof bootstrap.Modal !== 'undefined' && bootstrap.Modal.VERSION >= "5") {
444
- bootstrap.Modal.getOrCreateInstance(el || document.querySelector(Blacklight.modal.modalSelector)).show();
445
- } else {
446
- $(el || Blacklight.modal.modalSelector).modal('show');
447
- }
448
- };
449
- Blacklight.onLoad(function () {
450
- Blacklight.modal.setupModal();
451
- });
452
- Blacklight.doSearchContextBehavior = function () {
453
- if (typeof Blacklight.do_search_context_behavior == 'function') {
454
- console.warn("do_search_context_behavior is deprecated. Use doSearchContextBehavior instead.");
455
- return Blacklight.do_search_context_behavior();
112
+ get labelTarget() {
113
+ return this.form.querySelector('[data-checkboxsubmit-target="label"]')
114
+ }
115
+
116
+ get checkboxTarget() {
117
+ return this.form.querySelector('[data-checkboxsubmit-target="checkbox"]')
118
+ }
119
+
120
+ get spanTarget() {
121
+ return this.form.querySelector('[data-checkboxsubmit-target="span"]')
122
+ }
123
+
124
+ updateStateFor(state) {
125
+ this.checkboxTarget.checked = state;
126
+
127
+ if (state) {
128
+ this.labelTarget.classList.add('checked');
129
+ //Set the Rails hidden field that fakes an HTTP verb
130
+ //properly for current state action.
131
+ this.formTarget.querySelector('input[name=_method]').value = 'delete';
132
+ this.spanTarget.innerHTML = this.form.getAttribute('data-present');
133
+ } else {
134
+ this.labelTarget.classList.remove('checked');
135
+ this.formTarget.querySelector('input[name=_method]').value = 'put';
136
+ this.spanTarget.innerHTML = this.form.getAttribute('data-absent');
137
+ }
138
+ }
456
139
  }
457
- const elements = document.querySelectorAll('a[data-context-href]');
458
- // Equivalent to Array.from(), but supports ie11
459
- const nodes = Array.prototype.slice.call(elements);
460
- nodes.forEach(function (element) {
461
- element.addEventListener('click', function (e) {
462
- Blacklight.handleSearchContextMethod.call(e.currentTarget, e);
140
+
141
+ const BookmarkToggle = (() => {
142
+ // change form submit toggle to checkbox
143
+ Blacklight.doBookmarkToggleBehavior = function() {
144
+ document.addEventListener('click', (e) => {
145
+ if (e.target.matches('[data-checkboxsubmit-target="checkbox"]')) {
146
+ const form = e.target.closest('form');
147
+ if (form) new CheckboxSubmit(form).clicked(e);
148
+ }
149
+ });
150
+ };
151
+ Blacklight.doBookmarkToggleBehavior.selector = 'form.bookmark-toggle';
152
+
153
+ Blacklight.doBookmarkToggleBehavior();
154
+ })();
155
+
156
+ const ButtonFocus = (() => {
157
+ document.addEventListener('click', (e) => {
158
+ // Button clicks should change focus. As of 10/3/19, Firefox for Mac and
159
+ // Safari both do not set focus to a button on button click.
160
+ // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information
161
+ if (e.target.matches('[data-toggle="collapse"]') || e.target.matches('[data-bs-toggle="collapse"]')) {
162
+ e.target.focus();
163
+ }
463
164
  });
464
- });
465
- };
466
-
467
- // this is the Rails.handleMethod with a couple adjustments, described inline:
468
- // first, we're attaching this directly to the event handler, so we can check for meta-keys
469
- Blacklight.handleSearchContextMethod = function (event) {
470
- if (typeof Blacklight.handle_search_context_method == 'function') {
471
- console.warn("handle_search_context_method is deprecated. Use handleSearchContextMethod instead.");
472
- return Blacklight.handle_search_context_method(event);
473
- }
474
- var link = this;
475
-
476
- // instead of using the normal href, we need to use the context href instead
477
- let href = link.getAttribute('data-context-href');
478
- let target = link.getAttribute('target');
479
- let csrfToken = Blacklight.csrfToken();
480
- let csrfParam = Blacklight.csrfParam();
481
- let form = document.createElement('form');
482
- form.method = 'post';
483
- form.action = href;
484
- let formContent = "<input name=\"_method\" value=\"post\" type=\"hidden\" />\n <input name=\"redirect\" value=\"".concat(link.getAttribute('href'), "\" type=\"hidden\" />");
485
-
486
- // check for meta keys.. if set, we should open in a new tab
487
- if (event.metaKey || event.ctrlKey) {
488
- form.dataset.turbo = "false";
489
- target = '_blank';
490
- }
491
- if (csrfParam !== undefined && csrfToken !== undefined) {
492
- formContent += "<input name=\"".concat(csrfParam, "\" value=\"").concat(csrfToken, "\" type=\"hidden\" />");
493
- }
165
+ })();
166
+
167
+ /*
168
+ The blacklight modal plugin can display some interactions inside a Bootstrap
169
+ modal window, including some multi-page interactions.
170
+
171
+ It supports unobtrusive Javascript, where a link or form that would have caused
172
+ a new page load is changed to display it's results inside a modal dialog,
173
+ by this plugin. The plugin assumes there is a Bootstrap modal div
174
+ on the page with id #blacklight-modal to use as the modal -- the standard Blacklight
175
+ layout provides this.
176
+
177
+ To make a link or form have their results display inside a modal, add
178
+ `data-blacklight-modal="trigger"` to the link or form. (Note, form itself not submit input)
179
+ With Rails link_to helper, you'd do that like:
180
+
181
+ link_to something, link, data: { blacklight_modal: "trigger" }
182
+
183
+ The results of the link href or form submit will be displayed inside
184
+ a modal -- they should include the proper HTML markup for a bootstrap modal's
185
+ contents. Also, you ordinarily won't want the Rails template with wrapping
186
+ navigational elements to be used. The Rails controller could suppress
187
+ the layout when a JS AJAX request is detected, OR the response
188
+ can include a `<div data-blacklight-modal="container">` -- only the contents
189
+ of the container will be placed inside the modal, the rest of the
190
+ page will be ignored.
191
+
192
+ Link or forms inside the modal will ordinarily cause page loads
193
+ when they are triggered. However, if you'd like their results
194
+ to stay within the modal, just add `data-blacklight-modal="preserve"`
195
+ to the link or form.
196
+
197
+ Here's an example of what might be returned, demonstrating most of the devices available:
198
+
199
+ <div data-blacklight-modal="container">
200
+ <div class="modal-header">
201
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
202
+ <h3 class="modal-title">Request Placed</h3>
203
+ </div>
204
+
205
+ <div class="modal-body">
206
+ <p>Some message</p>
207
+ <%= link_to "This result will still be within modal", some_link, data: { blacklight_modal: "preserve" } %>
208
+ </div>
209
+
210
+
211
+ <div class="modal-footer">
212
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
213
+ </div>
214
+ </div>
494
215
 
495
- // Must trigger submit by click on a button, else "submit" event handler won't work!
496
- // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
497
- formContent += '<input type="submit" />';
498
- if (target) {
499
- form.setAttribute('target', target);
500
- }
501
- form.style.display = 'none';
502
- form.innerHTML = formContent;
503
- document.body.appendChild(form);
504
- form.querySelector('[type="submit"]').click();
505
- event.preventDefault();
506
- event.stopPropagation();
507
- };
508
- Blacklight.onLoad(function () {
509
- Blacklight.doSearchContextBehavior();
510
- });
511
216
 
217
+ One additional feature. If the content returned from the AJAX form submission
218
+ can be a turbo-stream that defines some HTML fragementsand where on the page to put them:
219
+ https://turbo.hotwired.dev/handbook/streams
220
+ */
221
+
222
+ const Modal = (() => {
223
+ // We keep all our data in Blacklight.modal object.
224
+ // Create lazily if someone else created first.
225
+ if (Blacklight.modal === undefined) {
226
+ Blacklight.modal = {};
227
+ }
228
+
229
+ const modal = Blacklight.modal;
230
+
231
+ // a Bootstrap modal div that should be already on the page hidden
232
+ modal.modalSelector = '#blacklight-modal';
233
+
234
+ // Trigger selectors identify forms or hyperlinks that should open
235
+ // inside a modal dialog.
236
+ modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';
237
+
238
+ // preserve selectors identify forms or hyperlinks that, if activated already
239
+ // inside a modal dialog, should have destinations remain inside the modal -- but
240
+ // won't trigger a modal if not already in one.
241
+ //
242
+ // No need to repeat selectors from trigger selectors, those will already
243
+ // be preserved. MUST be manually prefixed with the modal selector,
244
+ // so they only apply to things inside a modal.
245
+ modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';
246
+
247
+ modal.containerSelector = '[data-blacklight-modal~=container]';
248
+
249
+ // Called on fatal failure of ajax load, function returns content
250
+ // to show to user in modal. Right now called only for extreme
251
+ // network errors.
252
+ modal.onFailure = function (jqXHR, textStatus, errorThrown) {
253
+ console.error('Server error:', this.url, jqXHR.status, errorThrown);
254
+
255
+ const contents = `<div class="modal-header">
256
+ <div class="modal-title">There was a problem with your request.</div>
257
+ <button type="button" class="blacklight-modal-close btn-close close" data-dismiss="modal" data-bs-dismiss="modal" aria-label="Close">
258
+ <span aria-hidden="true">&times;</span>
259
+ </button>
260
+ </div>
261
+ <div class="modal-body">
262
+ <p>Expected a successful response from the server, but got an error</p>
263
+ <pre>${this.type} ${this.url}\n${jqXHR.status}: ${errorThrown}</pre>
264
+ </div>`;
265
+
266
+ document.querySelector(`${modal.modalSelector} .modal-content`).innerHTML = contents;
267
+
268
+ modal.show();
269
+ };
270
+
271
+ // Add the passed in contents to the modal and display it.
272
+ modal.receiveAjax = function (contents) {
273
+ const domparser = new DOMParser();
274
+ const dom = domparser.parseFromString(contents, "text/html");
275
+ const elements = dom.querySelectorAll(`${modal.containerSelector} > *`);
276
+ document.querySelector(`${modal.modalSelector} .modal-content`).replaceChildren(...elements);
277
+
278
+ modal.show();
279
+ };
280
+
281
+
282
+ modal.modalAjaxLinkClick = function(e) {
283
+ e.preventDefault();
284
+ const href = e.target.getAttribute('href');
285
+ fetch(href)
286
+ .then(response => {
287
+ if (!response.ok) {
288
+ throw new TypeError("Request failed");
289
+ }
290
+ return response.text();
291
+ })
292
+ .then(data => modal.receiveAjax(data))
293
+ .catch(error => modal.onFailure(error));
294
+ };
295
+
296
+ modal.setupModal = function() {
297
+ // Register both trigger and preserve selectors in ONE event handler, combining
298
+ // into one selector with a comma, so if something matches BOTH selectors, it
299
+ // still only gets the event handler called once.
300
+ document.addEventListener('click', (e) => {
301
+ if (e.target.matches(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))
302
+ modal.modalAjaxLinkClick(e);
303
+ else if (e.target.matches('[data-bl-dismiss="modal"]'))
304
+ modal.hide();
305
+ });
306
+ };
307
+
308
+ modal.hide = function (el) {
309
+ const dom = document.querySelector(Blacklight.modal.modalSelector);
310
+
311
+ if (!dom.open) return
312
+ dom.close();
313
+ };
314
+
315
+ modal.show = function(el) {
316
+ const dom = document.querySelector(Blacklight.modal.modalSelector);
317
+
318
+ if (dom.open) return
319
+ dom.showModal();
320
+ };
321
+
322
+ modal.setupModal();
323
+ })();
324
+
325
+ const SearchContext = (() => {
326
+ Blacklight.doSearchContextBehavior = function() {
327
+ document.addEventListener('click', (e) => {
328
+ if (e.target.matches('[data-context-href]')) {
329
+ Blacklight.handleSearchContextMethod.call(e.target, e);
330
+ }
331
+ });
332
+ };
333
+
334
+ Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content;
335
+ Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content;
336
+
337
+ // this is the Rails.handleMethod with a couple adjustments, described inline:
338
+ // first, we're attaching this directly to the event handler, so we can check for meta-keys
339
+ Blacklight.handleSearchContextMethod = function(event) {
340
+ const link = this;
341
+
342
+ // instead of using the normal href, we need to use the context href instead
343
+ let href = link.getAttribute('data-context-href');
344
+ let target = link.getAttribute('target');
345
+ let csrfToken = Blacklight.csrfToken();
346
+ let csrfParam = Blacklight.csrfParam();
347
+ let form = document.createElement('form');
348
+ form.method = 'post';
349
+ form.action = href;
350
+
351
+
352
+ let formContent = `<input name="_method" value="post" type="hidden" />
353
+ <input name="redirect" value="${link.getAttribute('href')}" type="hidden" />`;
354
+
355
+ // check for meta keys.. if set, we should open in a new tab
356
+ if(event.metaKey || event.ctrlKey) {
357
+ target = '_blank';
358
+ }
359
+
360
+ if (csrfParam !== undefined && csrfToken !== undefined) {
361
+ formContent += `<input name="${csrfParam}" value="${csrfToken}" type="hidden" />`;
362
+ }
363
+
364
+ // Must trigger submit by click on a button, else "submit" event handler won't work!
365
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
366
+ formContent += '<input type="submit" />';
367
+
368
+ if (target) { form.setAttribute('target', target); }
369
+
370
+ form.style.display = 'none';
371
+ form.innerHTML = formContent;
372
+ document.body.appendChild(form);
373
+ form.querySelector('[type="submit"]').click();
374
+
375
+ event.preventDefault();
376
+ };
377
+
378
+ Blacklight.doSearchContextBehavior();
379
+ })();
380
+
381
+ const index = {
382
+ BookmarkToggle,
383
+ ButtonFocus,
384
+ Modal,
385
+ SearchContext,
386
+ onLoad: Blacklight.onLoad
387
+ };
388
+
389
+ return index;
390
+
391
+ }));
392
+ //# sourceMappingURL=blacklight.js.map