diff --git a/app/assets/stylesheets/bioportal.scss b/app/assets/stylesheets/bioportal.scss index a00e324f2..49d079975 100644 --- a/app/assets/stylesheets/bioportal.scss +++ b/app/assets/stylesheets/bioportal.scss @@ -651,6 +651,7 @@ div.tree_error { #bd ul.simpleTree li{ margin-left:-10px; + padding: 5px 0 0 10px; } #bd ul.simpleTree{ @@ -668,6 +669,22 @@ div.tree_error { overflow:auto; border: 1px solid #444444; */ + a.tree-link { + display: inline-block; + padding: 5px; + } + + a.tree-link.active { + cursor: default; + background-color: var(--light-color); + font-weight: bold; + border-radius: 2px; + } + + .tree-border-left{ + border-left: 2px dotted #eee; + margin-left: 6px; + } } .simpleTree li { list-style: none; diff --git a/app/components/tree_infinite_scroll_component/tree_infinite_scroll_component.html.haml b/app/components/tree_infinite_scroll_component/tree_infinite_scroll_component.html.haml index cbaaba03a..386fa14dd 100644 --- a/app/components/tree_infinite_scroll_component/tree_infinite_scroll_component.html.haml +++ b/app/components/tree_infinite_scroll_component/tree_infinite_scroll_component.html.haml @@ -4,10 +4,8 @@ current_page: @current_page, next_page: @next_page) do |c| %div - %ul.simpleTree{data:{controller: 'simple-tree','simple-tree': { 'auto-click-value': auto_click? }, action: 'clicked->history#updateURL'}} - %li.root - %ul - = content + = render TreeViewComponent.new(id: nil, auto_click: auto_click?) do + = content - c.error do %div.text-wrap diff --git a/app/components/tree_link_component.rb b/app/components/tree_link_component.rb new file mode 100644 index 000000000..17334ea57 --- /dev/null +++ b/app/components/tree_link_component.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +class TreeLinkComponent < ViewComponent::Base + include MultiLanguagesHelper + include ComponentsHelper + + def initialize(child:, href:, children_href: , selected_child: , data: {}, muted: false, target_frame: nil) + @child = child + @selected_child = selected_child + @active_style = child.id.eql?(selected_child&.id) && 'active' + #@icons = child.relation_icon(node) + @muted_style = muted ? 'text-muted' : '' + @href = href + @children_link = children_href + if @child.prefLabel.nil? + @pref_label_html = child.id.split('/').last + else + pref_label_lang, @pref_label_html = select_language_label(@child.prefLabel) + pref_label_lang = pref_label_lang.to_s.upcase + @tooltip = pref_label_lang.eql?("@NONE") ? "" : pref_label_lang + end + @data ||= { controller: 'tooltip', 'tooltip-position-value': 'right', turbo: true, 'turbo-frame': target_frame, action: 'click->simple-tree#select'} + + @data.merge!(data) do |_, old, new| + "#{old} #{new}" + end + end + + + # This gives a very hacky short code to use to uniquely represent a class + # based on its parent in a tree. Used for unique ids in HTML for the tree view + def short_uuid + rand(36 ** 8).to_s(36) + end + + # TDOD check where used + def child_id + @child.id.to_s.split('/').last + end + + def open? + @child.expanded? ? 'open' : '' + end + + def border_left + !@child.hasChildren && 'pl-3 tree-border-left' + end + + def li_id + @child.id.eql?('bp_fake_root') ? 'bp_fake_root' : short_uuid + end + + + def open_children_link + return unless @child.hasChildren + if @child.expanded? + tree_close_icon + else + content_tag('turbo_frame', id: "#{child_id}_open_link") do + link_to @children_link, + data: { turbo: true, turbo_frame: "#{child_id + '_childs'}" } do + content_tag(:i, nil, class: "fas fa-chevron-right") + end + end + end + + end + +end diff --git a/app/components/tree_link_component/tree_link_component.html.haml b/app/components/tree_link_component/tree_link_component.html.haml new file mode 100644 index 000000000..2bf3815b0 --- /dev/null +++ b/app/components/tree_link_component/tree_link_component.html.haml @@ -0,0 +1,10 @@ +%li{id:li_id , class: open?} + = open_children_link + %a{id: @child.id, data: @data, title: @tooltip, + href: @href, class: "tree-link #{@muted_style} #{@active_style} #{border_left} #{open?}"} + = @pref_label_html + + - if @child.hasChildren && !@child.expanded? + = render TurboFrameComponent.new(id: "#{child_id}_childs") + - elsif @child.expanded? + = content \ No newline at end of file diff --git a/app/components/tree_view_component.rb b/app/components/tree_view_component.rb new file mode 100644 index 000000000..54e03c129 --- /dev/null +++ b/app/components/tree_view_component.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class TreeViewComponent < ViewComponent::Base + include Turbo::FramesHelper + include ComponentsHelper + + renders_many :children, TreeLinkComponent + + def initialize(id:, auto_click: false, sub_tree: false, **html_options) + @id = id + @auto_click = auto_click + @html_options = html_options + @sub_tree = sub_tree + end + + private + + def sub_tree? + @sub_tree + end + + def tree_container(&block) + if sub_tree? + content_tag(:ul, capture(&block), class: 'pl-2 tree-border-left') + else + content_tag(:div, class: 'tree_wrapper hide-if-loading') do + content_tag(:ul, capture(&block), class: 'simpleTree root', data: { controller: 'simple-tree', + 'simple-tree-auto-click-value': "#{auto_click?}", + action: 'clicked->history#updateURL' }) + end + end + end + + def auto_click? + @auto_click.to_s + end + + # TDOD check where used + def child_id(child) + child.id.to_s.split('/').last + end + +end + \ No newline at end of file diff --git a/app/components/tree_view_component/tree_view_component.html.haml b/app/components/tree_view_component/tree_view_component.html.haml new file mode 100644 index 000000000..c8b47d1a4 --- /dev/null +++ b/app/components/tree_view_component/tree_view_component.html.haml @@ -0,0 +1,9 @@ +%turbo-frame{id: "#{@id}", **@html_options} + = tree_container do + - children.each do |child| + = child + = content + + + + diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1420356bb..d01e9348f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -157,12 +157,6 @@ def to_param(name) # Paramaterizes URLs without encoding end end - def undo_param(name) #Undo Paramaterization - unless name.nil? - name.to_s.gsub('_'," ") - end - end - def bp_config_json # For config settings, see # config/bioportal_config.rb @@ -188,22 +182,6 @@ def bp_config_json config.to_json end - def remote_file_exists?(url) - begin - url = URI.parse(url) - - if url.kind_of?(URI::FTP) - check = check_ftp_file(url) - else - check = check_http_file(url) - end - - rescue - return false - end - - check - end def rest_url # Split the URL into protocol and path parts @@ -308,15 +286,6 @@ def redirect_to_home # Redirect to Home Page redirect_to "/" end - def redirect_to_history # Redirects to the correct tab through the history system - if session[:redirect].nil? - redirect_to_home - else - tab = find_tab(session[:redirect][:ontology]) - session[:redirect]=nil - redirect_to uri_url(:ontology=>tab.ontology_id,:conceptid=>tab.concept) - end - end def redirect_new_api(class_view = false) # Hack to make ontologyid and conceptid work in addition to id and ontology params @@ -373,24 +342,6 @@ def authorize_and_redirect end end - # Verifies that a user owns an object - def authorize_owner(id=nil) - if id.nil? - id = params[:id].to_i - end - - id.map! {|i| i.to_i} if id.kind_of?(Array) - - if session[:user].nil? - redirect_to_home - else - if id.kind_of?(Array) - redirect_to_home if !session[:user].admin? && !id.include?(session[:user].id.to_i) - else - redirect_to_home if !session[:user].admin? && !session[:user].id.to_i.eql?(id) - end - end - end def authorize_admin admin = session[:user] && session[:user].admin? @@ -405,41 +356,7 @@ def ontology_restricted?(acronym) restrict_downloads = $NOT_DOWNLOADABLE restrict_downloads.include? acronym end - # updates the 'history' tab with the current selected concept - def update_tab(ontology, concept) - array = session[:ontologies] || [] - found = false - for item in array - if item.ontology_id.eql?(ontology.id) - item.concept=concept - found=true - end - end - - unless found - array << History.new(ontology.id, ontology.name, ontology.acronym, concept) - end - - session[:ontologies]=array - end - - # Removes a 'history' tab - def remove_tab(ontology_id) - array = session[:ontologies] - array.delete(find_tab(ontology_id)) - session[:ontologies]=array - end - # Returns a specific 'history' tab - def find_tab(ontology_id) - array = session[:ontologies] - for item in array - if item.ontology_id.eql?(ontology_id) - return item - end - end - return nil - end def check_delete_mapping_permission(mappings) # ensure mappings is an Array of mappings (some calls may provide only a single mapping instance) @@ -463,13 +380,13 @@ def using_captcha? def get_class(params) lang = request_lang - + if @ontology.flat? ignore_concept_param = params[:conceptid].nil? || - params[:conceptid].empty? || - params[:conceptid].eql?("root") || - params[:conceptid].eql?("bp_fake_root") + params[:conceptid].empty? || + params[:conceptid].eql?("root") || + params[:conceptid].eql?("bp_fake_root") if ignore_concept_param # Don't display any classes in the tree @concept = LinkedData::Client::Models::Class.new @@ -491,19 +408,19 @@ def get_class(params) # not ignoring 'bp_fake_root' here include = 'prefLabel,hasChildren,obsolete' ignore_concept_param = params[:conceptid].nil? || - params[:conceptid].empty? || - params[:conceptid].eql?("root") + params[:conceptid].empty? || + params[:conceptid].eql?("root") if ignore_concept_param # get the top level nodes for the root # TODO_REV: Support views? Replace old view call: @ontology.top_level_classes(view) - @roots = @ontology.explore.roots(concept_schemes: params[:concept_schemes]) + @roots = @ontology.explore.roots(concept_schemes: params[:concept_schemes]) if @roots.nil? || @roots.empty? LOG.add :debug, "Missing @roots for #{@ontology.acronym}" classes = @ontology.explore.classes.collection @concept = classes.first.explore.self(full: true) if classes.first return end - + @root = LinkedData::Client::Models::Class.new(read_only: true) @root.children = @roots.sort{|x,y| (x.prefLabel || "").downcase <=> (y.prefLabel || "").downcase} @@ -569,17 +486,7 @@ def get_simplified_ontologies_hash() return simple_ontologies end - def get_ontology_details(ont_uri) - # Note the simplify_ontology_model will cache individual ontology data. - begin - ont_model = LinkedData::Client::Models::Ontology.find(ont_uri) - ont = simplify_ontology_model(ont_model) - rescue Exception => e - LOG.add :error, e.message - return nil - end - return ont - end + def simplify_classes(classes) # Simplify the classes batch service data for the UI diff --git a/app/controllers/concepts_controller.rb b/app/controllers/concepts_controller.rb index 6cd525453..c8c734e34 100644 --- a/app/controllers/concepts_controller.rb +++ b/app/controllers/concepts_controller.rb @@ -3,6 +3,8 @@ class ConceptsController < ApplicationController include MappingsHelper include ConceptsHelper + include TurboHelper + layout 'ontology' def show_concept @@ -21,9 +23,9 @@ def show_concept @ob_instructions = helpers.ontolobridge_instructions_template(@ontology) @concept = @ontology.explore.single_class({full: true, language: request_lang}, params[:id]) @instances_concept_id = @concept.id - concept_not_found(params[:id]) if @concept.nil? - gather_details + @notes = @concept.explore.notes + render :partial => 'show' end @@ -40,22 +42,22 @@ def show @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology]).first @ob_instructions = helpers.ontolobridge_instructions_template(@ontology) - if request.xhr? - display = params[:callback].eql?('load') ? {full: true} : {display: "prefLabel"} - @concept = @ontology.explore.single_class(display, params[:id]) - concept_not_found(params[:id]) if @concept.nil? - @schemes = params[:concept_schemes]&.split(',') - show_ajax_request # process an ajax call - else - # Get the latest 'ready' submission, or fallback to any latest submission - # TODO: change the logic here if the fallback will crash the visualization + # Get the latest 'ready' submission, or fallback to any latest submission + # TODO: change the logic here if the fallback will crash the visualization @submission = get_ontology_submission_ready(@ontology) # application_controller @concept = @ontology.explore.single_class({full: true}, params[:id]) - concept_not_found(params[:id]) if @concept.nil? - @schemes = params[:concept_schemes].split(',') - show_ajax_request # process a full call - end + concept_not_found(params[:id]) if @concept.nil? + @schemes = params[:concept_schemes].split(',') + + @concept.children = @concept.explore.children(pagesize: 750, concept_schemes: Array(@schemes).join(','), language: request_lang, display: 'prefLabel,obsolete,hasChildren').collection || [] + @concept.children.sort! { |x, y| (x.prefLabel || "").downcase <=> (y.prefLabel || "").downcase } unless @concept.children.empty? + render turbo_stream: [ + replace(helpers.child_id(@concept) + '_open_link') { helpers.tree_close_icon }, + replace(helpers.child_id(@concept) + '_childs') do + helpers.concepts_tree_component(@concept, @concept, @ontology.acronym, Array(@schemes), request_lang, sub_tree: true) + end + ] end def show_label @@ -94,9 +96,11 @@ def show_tree @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology]).first if @ontology.nil? ontology_not_found(params[:ontology]) - else + else get_class(params) #application_controller - render partial: 'ontologies/treeview', locals: { autoCLick: params[:auto_click] || true } + render inline: helpers.concepts_tree_component(@root, @concept, + @ontology.acronym, params[:concept_schemes].split(','), request_lang, + id: 'concepts_tree_view', auto_click: params[:auto_click] || true) end end @@ -180,39 +184,12 @@ def biomixer render partial: "biomixer", layout: false end -# PRIVATE ----------------------------------------- -private - - def show_ajax_request - case params[:callback] - when 'load' # Load pulls in all the details of a node - gather_details - render :partial => 'load' - when 'children' # Children is called only for drawing the tree - @children = @concept.explore.children(pagesize: 750, concept_schemes: Array(@schemes).join(','), language: request_lang, display: 'prefLabel,obsolete,hasChildren').collection || [] - @children.sort! { |x, y| (x.prefLabel || "").downcase <=> (y.prefLabel || "").downcase } unless @children.empty? - render :partial => 'child_nodes' - end - end - # gathers the full set of data for a node - def show_uri_request - gather_details - build_tree - end + private - def gather_details - @notes = @concept.explore.notes - update_tab(@ontology, @concept.id) #updates the 'history' tab with the current node - end - def build_tree - # find path to root - rootNode = @concept.explore.tree(include: "prefLabel,hasChildren,obsolete,subClassOf") - @root = LinkedData::Client::Models::Class.new(read_only: true) - @root.children = rootNode unless rootNode.nil? - end + def filter_concept_with_no_date(concepts) concepts.filter { |c| !concept_date(c).nil?} end diff --git a/app/controllers/history_controller.rb b/app/controllers/history_controller.rb deleted file mode 100644 index 687839df5..000000000 --- a/app/controllers/history_controller.rb +++ /dev/null @@ -1,15 +0,0 @@ -class HistoryController < ApplicationController - - def remove # removes a 'history' tab - remove_tab(undo_param(params[:ontology])) - render :text =>"success" - end - - def update # updates the 'history' tab to point to the new node - ontology = DataAccess.getOntology(params[:ontology]) - update_tab(ontology,params[:concept]) - render :text =>"success" - end - - -end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e03ed69cb..fe240100e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -185,82 +185,8 @@ def draw_note_tree_leaves(notes,level,output,key) end end - def draw_tree(root, acronym, id = nil, concept_schemes = nil) - id = root.children.first.id if id.nil? - - # TODO: handle tree view for obsolete classes, e.g. 'http://purl.obolibrary.org/obo/GO_0030400' - raw build_tree(root, '', id, acronym, concept_schemes: concept_schemes) - end - - def build_tree(node, string, id, acronym, concept_schemes: nil) - - return string if node.children.nil? || node.children.empty? - - node.children.sort! { |a, b| (main_language_label(a.prefLabel) || a.id).downcase <=> (main_language_label(a.prefLabel) || b.id).downcase } - node.children.each do |child| - active_style = child.id.eql?(id) ? "active" : '' - - # This fake root will be present at the root of "flat" ontologies, we need to keep the id intact - - if child.id.eql?('bp_fake_root') - string << tree_link_to_concept(child: child, ontology_acronym: acronym, - active_style: active_style, node: node, skos: !concept_schemes.nil?) - else - string << tree_link_to_concept(child: child, ontology_acronym: acronym, - active_style: active_style, node: node, skos: !concept_schemes.nil?) - if child.hasChildren && !child.expanded? - string << tree_link_to_children(child: child, acronym: acronym, concept_schemes: concept_schemes) - elsif child.expanded? - string << '' - end - string << '' - end - end - string - end - - def tree_link_to_concept(child:, ontology_acronym:, active_style:, node: nil, skos: false) - language = request_lang - li_id = child.id.eql?('bp_fake_root') ? 'bp_fake_root' : short_uuid - open = child.expanded? ? "class='open'" : '' - #icons = child.relation_icon(node) removed because slow - muted_style = skos && Array(child.isInActiveScheme).empty? ? 'text-muted' : nil - muted_title = muted_style && !child.obsolete? ? "title='is not in a scheme'" : nil - href = ontology_acronym.blank? ? '#' : "/ontologies/#{ontology_acronym}/concepts/?id=#{CGI.escape(child.id)}&language=#{language}" - - if child.prefLabel.nil? - pref_label_html = child.id.split('/').last - else - pref_label_lang, pref_label_html = select_language_label(child.prefLabel) - pref_label_lang = pref_label_lang.to_s.upcase - tooltip = pref_label_lang.eql?("@NONE") ? "" : "data-controller='tooltip' data-tooltip-position-value='right' title='#{pref_label_lang}'"; - end - - link = <<-EOS - - #{ pref_label_html } - - EOS - "
  • #{link}" - end - - - def tree_link_to_children(child:, acronym: ,concept_schemes: nil) - language = request_lang - li_id = child.id.eql?('bp_fake_root') ? 'bp_fake_root' : short_uuid - concept_schemes = "&concept_schemes=#{concept_schemes.map{|x| CGI.escape(x)}.join(',')}" if concept_schemes - - link = "ajax_class" - "" + def child_id(child) + child.id.to_s.split('/').last end def loading_spinner(padding = false, include_text = true) @@ -272,11 +198,7 @@ def loading_spinner(padding = false, include_text = true) end end - # This gives a very hacky short code to use to uniquely represent a class - # based on its parent in a tree. Used for unique ids in HTML for the tree view - def short_uuid - rand(36**8).to_s(36) - end + def help_icon(link, html_attribs = {}) html_attribs["title"] ||= "Help" diff --git a/app/helpers/components_helper.rb b/app/helpers/components_helper.rb index c67209696..dfb275d5b 100644 --- a/app/helpers/components_helper.rb +++ b/app/helpers/components_helper.rb @@ -1,4 +1,21 @@ module ComponentsHelper + + def tree_link(child:, selected_concept: , href: ,concept_schemes: nil, data: {}, muted: false, target_frame: nil) + selected = child.id.eql?(selected_concept.id) + + render TreeLinkComponent.new(child: child, href: href, + active_style: selected && 'active', + concept_schemes: concept_schemes, muted: muted, + data: data, target_frame: target_frame + ) do + + end + end + + def tree_close_icon + content_tag(:i, nil, class: "fas fa-chevron-down text-primary", data:{action:'click->simple-tree#toggleChildren'}) + end + def info_tooltip(text) render Display::InfoTooltipComponent.new(text: text) end diff --git a/app/helpers/concepts_helper.rb b/app/helpers/concepts_helper.rb index 4418e6d52..c8087e365 100644 --- a/app/helpers/concepts_helper.rb +++ b/app/helpers/concepts_helper.rb @@ -1,5 +1,35 @@ # frozen_string_literal: true module ConceptsHelper + + def concept_tree_child_data(acronym, child, concept_schemes, data, language) + href = child.id.eql?('bp_fake_root') ? '#' : "/ontologies/#{acronym}/concepts/?id=#{CGI.escape(child.id)}&language=#{language}" + children_link = "/ajax_concepts/#{acronym}/?conceptid=#{CGI.escape(child.id)}&concept_schemes=#{concept_schemes.join(',')}&language=#{language}" + data = { + conceptid: child.id, + 'active-collections-value': child.isInActiveCollection || [], + 'collections-value': child.memberOf || [], + 'skos-collection-colors-target': 'collection', + }.merge(data) + [children_link, data, href] + end + def concepts_tree_component(root, selected_concept, acronym, concept_schemes, language, sub_tree: false, id: nil, data: {}, auto_click: false) + root.children.sort! { |a, b| (a.prefLabel || a.id).downcase <=> (b.prefLabel || b.id).downcase } + + render TreeViewComponent.new(id: id, sub_tree: sub_tree, auto_click: auto_click) do |tree_child| + root.children.each do |child| + children_link, data, href = concept_tree_child_data(acronym, child, concept_schemes, data, language) + + tree_child.child(child: child, href: href, + children_href: children_link, selected_child: selected_concept, + muted: child.isInActiveScheme&.empty?, + target_frame: 'concept_show', + data: data) do + concepts_tree_component(child, selected_concept, acronym, concept_schemes, language, sub_tree: true) + end + end + end + end + def exclude_relation?(relation_to_check, ontology = nil) excluded_relations = %w[type rdf:type [R] SuperClass InstanceCount] @@ -79,22 +109,25 @@ def same_period?(year, month, date) year.eql?(date.year) && month.eql?(date.strftime('%B')) end - def concepts_li_list(concepts) + def concepts_li_list(concepts, auto_click: false) out = '' concepts.each do |concept| - out += tree_link_to_concept(child: concept, ontology_acronym: @ontology.acronym, active_style: '') + children_link, data, href = concept_tree_child_data(@ontology.acronym, concept, [], {}, request_lang) + out += render TreeLinkComponent.new(child: concept, href: href, + children_href: '#', selected_child: concept.id.eql?(concepts.first.id) && auto_click ? concept : nil, + target_frame: 'concept_show', data: data) end out end - def render_concepts_by_dates + def render_concepts_by_dates(auto_click: false) return if @concepts_year_month.empty? first_year, first_month_concepts = @concepts_year_month.shift first_month, first_concepts = first_month_concepts.shift out = '' if same_period?(first_year, first_month, @last_date) - out += "" + out += "" else tmp = {} tmp[first_month] = first_concepts @@ -107,7 +140,7 @@ def render_concepts_by_dates @concepts_year_month.each do |year, month_concepts| month_concepts.each do |month, concepts| out += "" end end diff --git a/app/helpers/schemes_helper.rb b/app/helpers/schemes_helper.rb index 3e024feec..8a3e9a013 100644 --- a/app/helpers/schemes_helper.rb +++ b/app/helpers/schemes_helper.rb @@ -5,7 +5,7 @@ def get_schemes(ontology) end def get_scheme(ontology, scheme_uri) - ontology.explore.schemes({ include: 'all', language: request_lang}, scheme_uri) + ontology.explore.schemes({ include: 'all', language: request_lang }, scheme_uri) end def get_scheme_label(scheme) @@ -22,14 +22,14 @@ def get_schemes_labels(schemes, main_uri) selected_label = nil schemes_labels = [] - schemes.each do |x| + schemes.each do |x| id = x['@id'] label = select_language_label(get_scheme_label(x)) if id.eql? main_uri label[1] = "#{label[1]} (main)" unless label[0].empty? selected_label = { 'prefLabel' => label, '@id' => id } else - schemes_labels.append( { 'prefLabel' => label, '@id' => id }) + schemes_labels.append({ 'prefLabel' => label, '@id' => id }) end end @@ -45,7 +45,7 @@ def concept_label_to_show(submission: @submission_latest) def section_name(section) section = concept_label_to_show(submission: @submission_latest || @submission) if section.eql?('classes') section - #t("ontology_details.sections.#{section}" , section) + # t("ontology_details.sections.#{section}" , section) end def scheme_path(scheme_id = '', language = '') @@ -65,6 +65,7 @@ def no_main_scheme_alert 'no main scheme defined in the URI attribute' end end + def no_schemes_alert render Display::AlertComponent.new do "#{@ontology.acronym} does not contain schemes (skos:ConceptScheme)" @@ -72,37 +73,71 @@ def no_schemes_alert end def schemes_data - schemes_labels, main_scheme = get_schemes_labels(@schemes,@submission.URI) - selected_scheme = @schemes.select{ |s| params[:concept_schemes]&.split(',')&.include?(s['@id']) } + schemes_labels, main_scheme = get_schemes_labels(@schemes, @submission.URI) + selected_scheme = @schemes.select { |s| params[:concept_schemes]&.split(',')&.include?(s['@id']) } selected_scheme = selected_scheme.empty? ? [main_scheme] : selected_scheme [schemes_labels, main_scheme, selected_scheme] end - def tree_link_to_schemes(schemes_labels, main_scheme_label, selected_scheme_id) - out = '' + def scheme_tree_root(schemes_labels, main_scheme_label, selected_scheme_id) + selected_scheme = nil + schemes = sorted_labels(schemes_labels).map do |s| + next nil unless main_scheme_label.nil? || s['prefLabel'] != main_scheme_label['prefLabel'] + scheme = OpenStruct.new(s) + scheme.prefLabel = Array(get_scheme_label(s)).last + scheme.id = scheme['@id'] + selected_scheme = scheme if scheme.id.eql?(selected_scheme_id) + scheme + end.compact + + + main_scheme = nil + if main_scheme_label.nil? + children = schemes + else + main_scheme = OpenStruct.new(main_scheme_label) + main_scheme.prefLabel = Array(get_scheme_label(main_scheme_label)).last + main_scheme.children = schemes + main_scheme.id = main_scheme['@id'] + main_scheme['expanded?'] = true + main_scheme['hasChildren'] = true + children = [main_scheme] + end + root = OpenStruct.new + root.children = children - sorted_labels(schemes_labels).each do |s| - next unless main_scheme_label.nil? || s['prefLabel'] != main_scheme_label['prefLabel'] + [root, selected_scheme || main_scheme || root.children.first] + end - out << <<-EOS -
  • - #{link_to_scheme(s, selected_scheme_id)} -
  • - EOS - end - out + def scheme_tree_child_data(scheme, data, language) + href = scheme_path(scheme['@id'], language) rescue '' + data = { + schemeid: (scheme['@id'] rescue ''), + }.merge(data) + [data, href] end - def link_to_scheme(scheme, selected_scheme_id) - pref_label_lang, pref_label_html = get_scheme_label(scheme) - tooltip = pref_label_lang.to_s.eql?('@none') ? '' : "data-controller='tooltip' data-tooltip-position-value='right' title='#{pref_label_lang.upcase}'" - <<-EOS - - #{pref_label_html} - - EOS + def scheme_tree_component(root, selected_scheme, language = request_lang, id: nil, data: {}, sub_tree: false, auto_click: false) + root.children.sort! { |a, b| (a.prefLabel || a.id).downcase <=> (b.prefLabel || b.id).downcase } + + render TreeViewComponent.new(id: id, auto_click: auto_click, sub_tree: sub_tree) do |tree_child| + root.children.each do |child| + next if child.nil? + data, href = scheme_tree_child_data(child, data, language) + + tree_child.child(child: child, href: href, + children_href: '#', selected_child: selected_scheme, + target_frame: 'scheme', + data: data) do + scheme_tree_component(child, selected_scheme, language, sub_tree: true) + end + end + end end + + + end + + + diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 8d4ea6942..a722c875e 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -64,8 +64,7 @@ application.register("show-filter-count", ShowFilterCountController) import ShowModalController from "./show_modal_controller" application.register("show-modal", ShowModalController) -import SimpleTreeController from "./simple_tree_controller" -application.register("simple-tree", SimpleTreeController) + import SkosCollectionColorsController from "./skos_collection_colors_controller" application.register("skos-collection-colors", SkosCollectionColorsController) @@ -84,3 +83,6 @@ application.register("turbo-frame", TurboFrameController) import TurboFrameErrorController from "./turbo_frame_error_controller" application.register("turbo-frame-error", TurboFrameErrorController) + +import SimpleTreeController from "./simple_tree_controller" +application.register("simple-tree", SimpleTreeController) diff --git a/app/javascript/controllers/simple_tree_controller.js b/app/javascript/controllers/simple_tree_controller.js index bcc04cc7a..d1f9ea4f4 100644 --- a/app/javascript/controllers/simple_tree_controller.js +++ b/app/javascript/controllers/simple_tree_controller.js @@ -1,76 +1,61 @@ -import {Controller} from "@hotwired/stimulus" -import {useSimpleTree} from "../mixins/useSimpleTree"; - +import { Controller } from '@hotwired/stimulus' // Connects to data-controller="simple-tree" export default class extends Controller { - static values = { - autoClick: {type: Boolean, default: false} - } - - connect() { - this.simpleTreeCollection = useSimpleTree(this.element, - this.#afterClick.bind(this), - this.#afterAjaxError.bind(this), - this.#beforeAjax.bind(this) - ) - - - this.simpleTreeCollection.ready(() => { - let activeElem = this.element.querySelector('a.active') - if (activeElem) { - $(this.element).scrollTo($(activeElem)) - - if (this.autoClickValue) { - activeElem.click() - } - } - - - }) + static values = { + autoClick: { type: Boolean, default: false } + } - this.#onClickTooManyChildrenInit() - } - - #onClickTooManyChildrenInit() { - jQuery(".too_many_children_override").live('click', (event) => { - event.preventDefault(); - let result = jQuery(event.target).closest("ul"); - result.html(""); - jQuery.ajax({ - url: jQuery(event.target).attr('href'), - context: result, - success: function (data) { - this.html(data); - this.simpleTreeCollection.get(0).setTreeNodes(this); - }, - error: function () { - this.html("
    Problem getting children. Try again
    "); - } - }); - }); - } - - #afterClick(node) { - this.element.dispatchEvent(new CustomEvent('clicked', { - detail: { - node: node, - data: {...node.context.dataset} + connect () { + let activeElem = this.element.querySelector('a.active') + if (activeElem) { + $(this.element).scrollTo($(activeElem)) - } - })) + if (this.autoClickValue) { + activeElem.click() + } } - - #afterAjaxError(node) { - this.simpleTreeCollection[0].option.animate = false; - this.simpleTreeCollection.get(0).nodeToggle(node.parent()[0]); - if (node.parent().children(".expansion_error").length === 0) { - node.parent().append("Error, please try again"); + this.#onClickTooManyChildrenInit() + } + + select (event) { + this.element.querySelector('a.active')?.classList.toggle('active') + event.currentTarget.classList.toggle('active') + this.#afterClick(event.currentTarget) + } + + toggleChildren (event) { + event.preventDefault() + event.target.classList.toggle('fa-chevron-right') + event.target.classList.toggle('fa-chevron-down') + event.target.nextElementSibling.nextElementSibling.classList.toggle('hidden') + } + + #onClickTooManyChildrenInit () { + jQuery('.too_many_children_override').live('click', (event) => { + event.preventDefault() + let result = jQuery(event.target).closest('ul') + result.html('') + jQuery.ajax({ + url: jQuery(event.target).attr('href'), + context: result, + success: function (data) { + this.html(data) + this.simpleTreeCollection.get(0).setTreeNodes(this) + }, + error: function () { + this.html('
    Problem getting children. Try again
    ') } - this.simpleTreeCollection[0].option.animate = true; - } - - #beforeAjax(node) { - node.parent().children(".expansion_error").remove(); - } + }) + }) + } + + #afterClick (node) { + this.element.dispatchEvent(new CustomEvent('clicked', { + detail: { + node: node, + data: { ...node.dataset } + }, bubbles: true + })) + } } diff --git a/app/javascript/controllers/skos_collection_colors_controller.js b/app/javascript/controllers/skos_collection_colors_controller.js index bef363873..e2c5d4794 100644 --- a/app/javascript/controllers/skos_collection_colors_controller.js +++ b/app/javascript/controllers/skos_collection_colors_controller.js @@ -31,7 +31,7 @@ export default class extends Controller { collectionTargetConnected(collectionElem) { - if (this.selected.length > 0) { + if (this.selected && this.selected.length > 0) { this.#updateColorsTags(collectionElem) } } diff --git a/app/javascript/mixins/useHistory.js b/app/javascript/mixins/useHistory.js index f84ff4133..4fd271b94 100644 --- a/app/javascript/mixins/useHistory.js +++ b/app/javascript/mixins/useHistory.js @@ -1,6 +1,6 @@ export class HistoryService { - unWantedData = ['turbo', 'controller', 'target', 'value'] + unWantedData = ['turbo', 'controller', 'target', 'value', 'action'] constructor() { diff --git a/app/views/collections/_list_view.html.haml b/app/views/collections/_list_view.html.haml index bfe256748..5071f262e 100644 --- a/app/views/collections/_list_view.html.haml +++ b/app/views/collections/_list_view.html.haml @@ -5,9 +5,11 @@ no collections detected - else %div - %ul.simpleTree{data:{controller: 'simple-tree', 'simple-tree': { 'auto-click-value': "true" }, action: 'clicked->history#updateURL'}} - %li.root - %ul - - sort_collections_label(collections_labels).each do |c| - %li.doc - = raw link_to_collection(c, selected_collection_id) \ No newline at end of file + = render TreeViewComponent.new(id: nil, auto_click: true) do |tree_child| + - sort_collections_label(collections_labels).each do |collection| + - scheme = OpenStruct.new(collection) + - scheme.prefLabel = Array(get_collection_label(collection)).last + - scheme.id = scheme['@id'] + - tree_child.child(child: scheme, href: collection_path(collection['@id'], request_lang), + children_href: '#', selected_child: scheme.id.eql?(selected_collection_id) ? scheme : nil, + target_frame: 'collection', data: {collectionid: collection['@id']}) diff --git a/app/views/concepts/_child_nodes.html.haml b/app/views/concepts/_child_nodes.html.haml deleted file mode 100644 index 0137fa40a..000000000 --- a/app/views/concepts/_child_nodes.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- output ="" -- for child in @children - - output << tree_link_to_concept(child: child, ontology_acronym: @ontology.acronym, active_style: '', node: @concept, skos: !@schemes.nil?) - - if child.hasChildren - - output << tree_link_to_children(child: child, acronym: @ontology.acronym, concept_schemes: @schemes) - - output << "" -= raw output diff --git a/app/views/concepts/_date_sorted_list.html.haml b/app/views/concepts/_date_sorted_list.html.haml index 517b37590..95e79bf6e 100644 --- a/app/views/concepts/_date_sorted_list.html.haml +++ b/app/views/concepts/_date_sorted_list.html.haml @@ -1,6 +1,6 @@ = render TreeInfiniteScrollComponent.new(id: 'concepts_date_sorted_list', collection: @concepts, next_url: sorted_by_date_url(@page.nextPage, @concepts.last), current_page: @page.page, next_page: @page.nextPage) do |c| - = render_concepts_by_dates + = render_concepts_by_dates(auto_click: @page.page.eql?(1)) - c.error do The Classes/Concepts didn't define creation or modifications dates with dcterms diff --git a/app/views/concepts/_list.html.haml b/app/views/concepts/_list.html.haml index 34f4e9679..4a3c55a41 100644 --- a/app/views/concepts/_list.html.haml +++ b/app/views/concepts/_list.html.haml @@ -5,9 +5,11 @@ auto_click: @auto_click) do |c| - concepts = c.collection.sort_by{|concept| [concept.prefLabel&.capitalize || concept.id]} - if concepts && !concepts.empty? - = raw tree_link_to_concept(child: concepts.shift, ontology_acronym: @ontology.acronym, active_style: c.auto_click? ? 'active' : '') - concepts.each do |concept| - = raw tree_link_to_concept(child: concept, ontology_acronym: @ontology.acronym, active_style: '') + - children_link, data, href = concept_tree_child_data(@ontology.acronym, concept, [], {}, request_lang) + = render TreeLinkComponent.new(child: concept, href: href, + children_href: '#', selected_child: concept.id.eql?(concepts.first.id) && c.auto_click? ? concept : nil, + target_frame: 'concept_show', data: data) - c.error do The Collection didn't define any member diff --git a/app/views/ontologies/_treeview.html.haml b/app/views/ontologies/_treeview.html.haml deleted file mode 100644 index 86f1abdc0..000000000 --- a/app/views/ontologies/_treeview.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -= turbo_frame_tag 'concepts_tree_view' do - #tree_wrapper.hide-if-loading - %ul.simpleTree{data:{controller: 'simple-tree', - 'simple-tree': { 'auto-click-value': "#{autoCLick}" }, - action: 'clicked->history#updateURL'}} - %li.root - %ul - = draw_tree(@root, params[:ontology], @concept.id, params[:concept_schemes]&.split(',')) - - diff --git a/app/views/ontologies/concepts_browsers/_concepts_tree.html.haml b/app/views/ontologies/concepts_browsers/_concepts_tree.html.haml index fb23ac593..54d32eb5e 100644 --- a/app/views/ontologies/concepts_browsers/_concepts_tree.html.haml +++ b/app/views/ontologies/concepts_browsers/_concepts_tree.html.haml @@ -15,7 +15,7 @@ data: {action: 'changed->skos-collection-colors#updateCollectionTags' , 'chosen-enable-colors-value': 'true'}} -# Class tree - %div#sd_content.card.p-1.py-3{style: 'overflow-y: scroll; height: 60vh;'} + %div#sd_content.px-1{style: 'overflow-y: scroll; height: 60vh;'} - if skos? && @roots&.empty? %div.text-wrap = render Display::AlertComponent.new do @@ -24,4 +24,4 @@ - concept_schemes = skos? ? "&concept_schemes=#{params[:concept_schemes]}" : '' = render TurboFrameComponent.new(id: 'concepts_tree_view', src: "/ajax/classes/treeview?ontology=#{@ontology.acronym}&conceptid=#{escape(@concept.id)}#{concept_schemes}&auto_click=false&language=#{request_lang}", - data: {'turbo-frame-target': 'frame'}) \ No newline at end of file + data: {'turbo-frame-target': 'frame'}) \ No newline at end of file diff --git a/app/views/schemes/_tree_view.html.haml b/app/views/schemes/_tree_view.html.haml index d707d5655..80908351e 100644 --- a/app/views/schemes/_tree_view.html.haml +++ b/app/views/schemes/_tree_view.html.haml @@ -1,4 +1,3 @@ - - if no_schemes? %div = no_schemes_alert @@ -8,13 +7,6 @@ - if main_scheme_label.nil? = no_main_scheme_alert %div - %ul.simpleTree{data:{controller: 'simple-tree', action: 'clicked->history#updateURL'}} - %li.root - %ul - - if main_scheme_label.nil? - = raw tree_link_to_schemes(schemes_labels ,main_scheme_label, selected_scheme_id) - - else - %li.open - = raw link_to_scheme(main_scheme_label, selected_scheme_id) - %ul - = raw tree_link_to_schemes(schemes_labels ,main_scheme_label, selected_scheme_id) \ No newline at end of file + - root, selected_scheme = scheme_tree_root(schemes_labels, main_scheme_label, selected_scheme_id) + = scheme_tree_component(root, selected_scheme, auto_click: true) +