diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 35e541fa3..e4c794865 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -473,6 +473,16 @@ def categories_with_children(categories) parent_to_children end + def categories_with_parents(categories_children) + categories_parents = Hash.new { |hash, key| hash[key] = [] } + categories_children.each do |child, parents| + parents.each do |parent| + categories_parents[parent] << child + end + end + categories_parents + end + def id_to_acronym(id) id.split('/').last end diff --git a/app/helpers/submission_inputs_helper.rb b/app/helpers/submission_inputs_helper.rb index fb578578a..22b87e7a0 100644 --- a/app/helpers/submission_inputs_helper.rb +++ b/app/helpers/submission_inputs_helper.rb @@ -129,9 +129,13 @@ def ontology_administered_by_input(ontology = @ontology, users_list = @user_sele def ontology_categories_input(ontology = @ontology, categories = @categories) categories ||= LinkedData::Client::Models::Category.all(display_links: false, display_context: false) categories_children = categories_with_children(categories) + categories_parents = categories_with_parents(categories_children) render Input::InputFieldComponent.new(name: '', label: 'Categories') do - content_tag(:div, class: 'upload-ontology-chips-container', 'data-controller': 'parent-categories-selector', 'data-parent-categories-selector-categories-children-value': "#{categories_children.to_json}", 'data-parent-categories-selector-target': "chips") do + content_tag(:div, class: 'upload-ontology-chips-container', 'data-controller': 'parent-categories-selector', + 'data-parent-categories-selector-categories-children-value': "#{categories_children.to_json}", + 'data-parent-categories-selector-categories-parents-value': "#{categories_parents.to_json}", + 'data-parent-categories-selector-target': "chips") do hidden_field_tag('ontology[hasDomain][]') + categories.map do |category| content_tag(:div, 'data-action': 'click->parent-categories-selector#check') do diff --git a/app/javascript/controllers/parent_categories_selector_controller.js b/app/javascript/controllers/parent_categories_selector_controller.js index 2a639e5cf..5159c73b8 100644 --- a/app/javascript/controllers/parent_categories_selector_controller.js +++ b/app/javascript/controllers/parent_categories_selector_controller.js @@ -3,28 +3,50 @@ import { Controller } from "@hotwired/stimulus" // Connects to data-controller="parent-categories-selector" export default class extends Controller { static targets = ['chips'] - static values = { categoriesChildren: Object} + static values = { categoriesChildren: Object, categoriesParents: Object} - check(event){ - const input = event.currentTarget.querySelector('input') - const allInputs = this.chipsTarget.querySelectorAll('input') - const parents = this.categoriesChildrenValue - if(this.#id_to_acronym(input.value) in parents){ - const parentChildren = parents[this.#id_to_acronym(input.value)] - allInputs.forEach(i => { - if(parentChildren.includes(this.#id_to_acronym(i.value))){ - if(input.checked){ - i.checked = true; - i.dispatchEvent(new Event('change', { bubbles: true })); - } else { - i.checked = false; - i.dispatchEvent(new Event('change', { bubbles: true })); - } - } - }); + check(event) { + const input = event.currentTarget.querySelector('input'); + const allInputs = this.chipsTarget.querySelectorAll('input'); + const parents = this.categoriesChildrenValue; + const children = this.categoriesParentsValue; + const browseLogic = !this.hasCategoriesParentsValue; + + // Browse page logic: + // - Selecting a category will auto select all its children + // - Deselecting a category will auto deselect all its children + // Upload/Edit forms logic: + // - Selecting a category will auto select its parents + // - Deselecting a category will auto deselect its children + + const inputAcronym = this.#id_to_acronym(input.value); + + if (browseLogic) { + if (inputAcronym in parents) { + const parentChildren = parents[inputAcronym]; + this.#toggleInputState(allInputs, i => parentChildren.includes(this.#id_to_acronym(i.value)), input.checked); + } + } else { + if (inputAcronym in parents) { + const parentChildren = parents[inputAcronym]; + this.#toggleInputState(allInputs, i => parentChildren.includes(this.#id_to_acronym(i.value)), false); + } + if (inputAcronym in children) { + const childParents = children[inputAcronym]; + this.#toggleInputState(allInputs, i => childParents.includes(this.#id_to_acronym(i.value)), true); + } } } + #toggleInputState(inputs, condition, state) { + inputs.forEach(input => { + if (condition(input)) { + input.checked = state; + input.dispatchEvent(new Event('change', { bubbles: true })); + } + }); + } + #id_to_acronym(id){ return id.split('/').pop(); }