diff --git a/app/assets/images/icons/info.svg b/app/assets/images/icons/info.svg new file mode 100644 index 000000000..4232099d0 --- /dev/null +++ b/app/assets/images/icons/info.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/assets/images/icons/loop.svg b/app/assets/images/icons/loop.svg new file mode 100644 index 000000000..38a8ac694 --- /dev/null +++ b/app/assets/images/icons/loop.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/assets/stylesheets/bioportal.scss b/app/assets/stylesheets/bioportal.scss index 1003395d0..e20991b0c 100644 --- a/app/assets/stylesheets/bioportal.scss +++ b/app/assets/stylesheets/bioportal.scss @@ -1,6 +1,10 @@ -a{ +a { text-decoration: none !important; } +p { + margin-bottom: 0; +} + .alignright { float:right; } diff --git a/app/assets/stylesheets/components/card.scss b/app/assets/stylesheets/components/card.scss new file mode 100644 index 000000000..825f227cf --- /dev/null +++ b/app/assets/stylesheets/components/card.scss @@ -0,0 +1,5 @@ +.summary-card { + border: 1px solid #dfdfdf; + border-radius: 5px; + margin-top: 20px; +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/chip_button.scss b/app/assets/stylesheets/components/chip_button.scss index 70c385ddd..e795c43cc 100644 --- a/app/assets/stylesheets/components/chip_button.scss +++ b/app/assets/stylesheets/components/chip_button.scss @@ -5,11 +5,14 @@ color: #777777 !important; font-weight: 500; font-size: 15px; + line-height: 44px; + display: inline; } .chip_button_small{ font-size: 10px; padding: 5px; + line-height: unset; } .chip_button_container_clickable { @@ -20,4 +23,5 @@ color: var(--primary-color); font-weight: 500; font-size: 15px; + cursor: grab; } diff --git a/app/assets/stylesheets/components/dropdown.scss b/app/assets/stylesheets/components/dropdown.scss index d923eae5c..b9c73b7f4 100644 --- a/app/assets/stylesheets/components/dropdown.scss +++ b/app/assets/stylesheets/components/dropdown.scss @@ -6,7 +6,7 @@ font-size: 16px; color: #000000; cursor: pointer; - padding: 14px 20px; + padding: 0 14px 0 0; } .dropdown-container { diff --git a/app/assets/stylesheets/components/field_container.scss b/app/assets/stylesheets/components/field_container.scss index 98e7cad87..cd2b46ea7 100644 --- a/app/assets/stylesheets/components/field_container.scss +++ b/app/assets/stylesheets/components/field_container.scss @@ -1,7 +1,5 @@ .field-container { - display: flex; - flex-wrap: wrap; - align-items: center; + margin-bottom: 0.5rem; } @@ -9,7 +7,6 @@ .field-description_text a { color: #888888 !important; font-size: 15px; - margin-top: 10px; margin-bottom: 0; } diff --git a/app/assets/stylesheets/components/header.scss b/app/assets/stylesheets/components/header.scss new file mode 100644 index 000000000..30e464aba --- /dev/null +++ b/app/assets/stylesheets/components/header.scss @@ -0,0 +1,15 @@ +.header-component { + display: flex; + align-items: center; + margin-bottom: 5px; + font-weight: 550; + justify-content: space-between; + font-size: 16px; + color: #000000; + cursor: pointer; + width: 100%; + padding: 14px 20px; + p { + margin-bottom: 0; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/image.scss b/app/assets/stylesheets/components/image.scss new file mode 100644 index 000000000..97414ab03 --- /dev/null +++ b/app/assets/stylesheets/components/image.scss @@ -0,0 +1,22 @@ +.image-container { + position: relative; + .image-content{ + width: 100%; + object-fit: scale-down; + margin-bottom: 30px; + } + .loop_icon { + position: absolute; + width: 50px; + background-color: white; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + height: 50px; + padding: 10px; + bottom: 0; + right: 0; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/index.scss b/app/assets/stylesheets/components/index.scss index 55d9f7348..3f0b11557 100644 --- a/app/assets/stylesheets/components/index.scss +++ b/app/assets/stylesheets/components/index.scss @@ -19,4 +19,7 @@ @import 'pill_button'; @import "switch"; @import "table"; -@import "concept_details"; \ No newline at end of file +@import "concept_details"; +@import "card"; +@import "header"; +@import "image"; \ No newline at end of file diff --git a/app/assets/stylesheets/components/summary_section.scss b/app/assets/stylesheets/components/summary_section.scss index 370da6ae4..9592f305b 100644 --- a/app/assets/stylesheets/components/summary_section.scss +++ b/app/assets/stylesheets/components/summary_section.scss @@ -1,4 +1,5 @@ .card_title { + display: flex; font-size: 18px; margin-top: 40px; margin-bottom: 20px; @@ -7,12 +8,6 @@ text-decoration: underline; text-underline-offset: 12px; text-decoration-color: var(--primary-color); - text-decoration-thickness: 1px; + text-decoration-thickness: 2px; } } -.Summary-page-card { - border: 1px solid #dfdfdf; - border-radius: 5px; - padding: 20px 20px; - margin-top: 20px; -} \ No newline at end of file diff --git a/app/assets/stylesheets/components/table.scss b/app/assets/stylesheets/components/table.scss index c132a4c31..27869d6ee 100644 --- a/app/assets/stylesheets/components/table.scss +++ b/app/assets/stylesheets/components/table.scss @@ -4,6 +4,10 @@ border-spacing: 0; } +.table-layout-fixed{ + table-layout: fixed; +} + .table-content thead th{ background-color: hsl(0, 0%, 100%); border-bottom: 1px solid hsl(240, 4%, 85%); @@ -11,9 +15,12 @@ color: hsl(230, 13%, 9%); } +.table-content-borderless thead th { + border-bottom: none !important; +} .table-content td, .table-content th{ - padding: 12px 24px; + padding: 0.75rem; vertical-align: top; } diff --git a/app/assets/stylesheets/components/tabs_container.scss b/app/assets/stylesheets/components/tabs_container.scss index 87ef9291c..422a29872 100644 --- a/app/assets/stylesheets/components/tabs_container.scss +++ b/app/assets/stylesheets/components/tabs_container.scss @@ -5,6 +5,7 @@ & > div { display: flex; justify-content: space-between; + width: 100%; } } diff --git a/app/components/display/header_component.rb b/app/components/display/header_component.rb new file mode 100644 index 000000000..b814f55fc --- /dev/null +++ b/app/components/display/header_component.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class Display::HeaderComponent < ViewComponent::Base + + renders_one :text + + def initialize(text: nil, tooltip: nil) + super + @text = text + @info = tooltip + end + + def call + content_tag(:div, class: 'header-component') do + out = content_tag(:p, text || @text) + if @info && !@info.empty? + out = out + render(Display::InfoTooltipComponent.new(text: @info)) + end + out + end + end + +end diff --git a/app/components/display/image_component.rb b/app/components/display/image_component.rb new file mode 100644 index 000000000..36bcb10fe --- /dev/null +++ b/app/components/display/image_component.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class Display::ImageComponent < ViewComponent::Base + include ModalHelper + + def initialize(src: , title: '', enable_zoom: true) + super + @src = src + @title = title + @enable_zoom = enable_zoom + end + + def call + content_tag(:div, class: 'image-container ') do + depiction_with_modal(@src) + end + end + + def depiction_with_modal(depiction_url) + img_tag = image_tag(depiction_url, class: 'image-content') + loop_icon_tag = content_tag(:span , image_tag('icons/loop.svg'), class: 'loop_icon') + modal_url = "/ajax/images/show?url=#{depiction_url}" + modal_options = { data: { show_modal_title_value: @title, show_modal_size_value: 'modal-xl' } } + + if @enable_zoom + link_to_modal(nil, modal_url, modal_options) do + loop_icon_tag + img_tag + end + else + img_tag + end + + end +end diff --git a/app/components/display/info_tooltip_component.rb b/app/components/display/info_tooltip_component.rb new file mode 100644 index 000000000..12c3bf93d --- /dev/null +++ b/app/components/display/info_tooltip_component.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class Display::InfoTooltipComponent < ViewComponent::Base + + def initialize(text: ) + super + @text = text + end + def call + image_tag("icons/info.svg", data:{controller:'tooltip'}, title: @text) + end + +end diff --git a/app/components/dropdown_container_component.rb b/app/components/dropdown_container_component.rb index c9d0f68bd..897d708b9 100644 --- a/app/components/dropdown_container_component.rb +++ b/app/components/dropdown_container_component.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class DropdownContainerComponent < ViewComponent::Base - - def initialize(title:, id:) + renders_one :empty_state + def initialize(title:, id:, tooltip:nil) super @title = title @id = id diff --git a/app/components/dropdown_container_component/dropdown_container_component.html.haml b/app/components/dropdown_container_component/dropdown_container_component.html.haml index f612f152b..11c0dd2ee 100644 --- a/app/components/dropdown_container_component/dropdown_container_component.html.haml +++ b/app/components/dropdown_container_component/dropdown_container_component.html.haml @@ -1,7 +1,12 @@ .dropdown-container .dropdown-title-bar{"data-toggle" => "collapse", "data-target" => "##{@id}"} - %p.mb-0 - = @title - %img{alt: "", src: asset_path("summary/arrow-down.svg")} + = render Display::HeaderComponent.new(text: @title, tooltip: @tooltip) + = image_tag("summary/arrow-down.svg", class: 'ml-2') + .collapse{id: @id} - = content \ No newline at end of file + - if content && !content.empty? + = content + - else + = render Layout::ListComponent.new do |l| + - l.row do + = empty_state || 'no data' diff --git a/app/components/field_container_component.rb b/app/components/field_container_component.rb index 4769718ee..d45e42e0e 100644 --- a/app/components/field_container_component.rb +++ b/app/components/field_container_component.rb @@ -2,9 +2,14 @@ class FieldContainerComponent < ViewComponent::Base - def initialize(label:, value:) + renders_one :label + def initialize(label: nil, value: nil) super @label = label @value = value end + + def show? + content && !content.strip.empty? || (!@value.nil? && !@value.strip.empty?) + end end diff --git a/app/components/field_container_component/field_container_component.html.haml b/app/components/field_container_component/field_container_component.html.haml index dad407c18..c7da1e602 100644 --- a/app/components/field_container_component/field_container_component.html.haml +++ b/app/components/field_container_component/field_container_component.html.haml @@ -1,6 +1,9 @@ -%div - .field-container +- if show? + %div.field-container %p.field-description_text - = @label.humanize - %p.field-normal_text - = @value \ No newline at end of file + = label || @label.humanize + - if content + = content + - else + %span.field-normal_text + = @value diff --git a/app/components/layout/card_component.rb b/app/components/layout/card_component.rb new file mode 100644 index 000000000..ceebc820c --- /dev/null +++ b/app/components/layout/card_component.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class Layout::CardComponent < ViewComponent::Base + renders_one :header, Display::HeaderComponent + + def call + content_tag(:div, class: 'summary-card') do + out = '' + out = header if header? + raw(out.to_s + content) + end + end +end diff --git a/app/components/layout/horizontal_list_component.rb b/app/components/layout/horizontal_list_component.rb new file mode 100644 index 000000000..aed504b7a --- /dev/null +++ b/app/components/layout/horizontal_list_component.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class Layout::HorizontalListComponent < ViewComponent::Base + renders_many :elements + + def call + return if elements.empty? + + content_tag(:div, class: 'd-flex flex-wrap') do + out = '' + elements.each do |element| + out = out + content_tag(:div, element, class: 'mr-1 mb-1 text-truncate overflow-hidden') + end + raw out + end + end + +end diff --git a/app/components/layout/list_component.rb b/app/components/layout/list_component.rb new file mode 100644 index 000000000..edf75b865 --- /dev/null +++ b/app/components/layout/list_component.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class Layout::ListComponent < ViewComponent::Base + + renders_many :rows + + def call + return if rows.map(&:to_s).reject(&:empty?).empty? + + content_tag(:div, style: 'padding: 0px 20px 20px 20px;') do + out = "" + rows.each do |row| + next if row.nil? || row.to_s.empty? + out = out + content_tag(:div, row.to_s, class: 'mb-1') + end + out.html_safe + end + end + +end diff --git a/app/components/summary_section_component.rb b/app/components/summary_section_component.rb index bd292122d..0ca33932f 100644 --- a/app/components/summary_section_component.rb +++ b/app/components/summary_section_component.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class SummarySectionComponent < ViewComponent::Base + renders_many :action_links def initialize(title: , link: nil, link_title: nil, show_card: true) super diff --git a/app/components/summary_section_component/summary_section_component.html.haml b/app/components/summary_section_component/summary_section_component.html.haml index b1090e54f..c43356866 100644 --- a/app/components/summary_section_component/summary_section_component.html.haml +++ b/app/components/summary_section_component/summary_section_component.html.haml @@ -3,7 +3,17 @@ = @title - if @link %span - = link_to(@link, target: "_blank", "aria-label": @link_title , title: @link_title) do - %i.fas.fa-lg.fa-question-circle{style: "color: var(--primary-color)"} -%div{class: show_card? ? 'Summary-page-card' : ''} - = content \ No newline at end of file + = link_to(@link, target: "_blank") do + = render Display::InfoTooltipComponent.new(text: @link_title) + + - if action_links? + %span + = render Layout::HorizontalListComponent.new do |l| + - action_links.each { |link| l.element { raw link }} + +- if show_card? + = render Layout::CardComponent.new do + = content +- else + %div + = content \ No newline at end of file diff --git a/app/components/table_component.rb b/app/components/table_component.rb index 05a2f3a7c..9670464db 100644 --- a/app/components/table_component.rb +++ b/app/components/table_component.rb @@ -5,16 +5,26 @@ class TableComponent < ViewComponent::Base renders_one :header, TableRowComponent renders_many :rows, TableRowComponent - def initialize(id: '', stripped: true) + def initialize(id: '', stripped: true, borderless: false, layout_fixed: false ) super @id = id @stripped = stripped + @borderless = borderless + @layout_fixed = layout_fixed end def stripped_class @stripped ? 'table-content-stripped' : '' end + def borderless_class + @borderless ? 'table-content-borderless' : '' + end + + def layout_fixed_class + @layout_fixed ? 'table-layout-fixed' : '' + end + def add_row(*array, &block) self.row.create(*array, &block) end diff --git a/app/components/table_component/table_component.html.haml b/app/components/table_component/table_component.html.haml index 089870b44..ed479fb66 100644 --- a/app/components/table_component/table_component.html.haml +++ b/app/components/table_component/table_component.html.haml @@ -1,4 +1,4 @@ -%table.table-content{id: @id, class: stripped_class} +%table.table-content{id: @id, class: stripped_class + ' ' + borderless_class + ' ' + layout_fixed_class} %thead = header %tbody{id: "#{@id}_table_body"} diff --git a/app/components/table_row_component.rb b/app/components/table_row_component.rb index 90c995a2a..68620a05c 100644 --- a/app/components/table_row_component.rb +++ b/app/components/table_row_component.rb @@ -4,9 +4,10 @@ class TableRowComponent < ViewComponent::Base renders_many :cells, TableCellComponent - def initialize(id: '') + def initialize(id: '', class_css: '') super @id = id + @class_css = class_css end def create(*array, &block) diff --git a/app/components/table_row_component/table_row_component.html.haml b/app/components/table_row_component/table_row_component.html.haml index 55dad8c73..c25e45eee 100644 --- a/app/components/table_row_component/table_row_component.html.haml +++ b/app/components/table_row_component/table_row_component.html.haml @@ -1,4 +1,4 @@ -%tr{id: @id} +%tr{id: @id, class: @class_css} - cells.each do |cell| = cell = content \ No newline at end of file diff --git a/app/components/text_area_field_component/text_area_field_component.html.haml b/app/components/text_area_field_component/text_area_field_component.html.haml index 40226b7e5..6a58213be 100644 --- a/app/components/text_area_field_component/text_area_field_component.html.haml +++ b/app/components/text_area_field_component/text_area_field_component.html.haml @@ -1,5 +1,5 @@ %div{data:{controller:"text-truncate", 'text-truncate-more-text-value': @see_more_text , 'text-truncate-less-text-value': @see_less_text}} - %p.text-content{'data-text-truncate-target': 'content'} + %span.text-content{'data-text-truncate-target': 'content'} = @value - %p.see_more_text{data:{'text-truncate-target': 'button', 'action':"click->text-truncate#toggle"}} + %span.see_more_text{data:{'text-truncate-target': 'button', 'action':"click->text-truncate#toggle"}} = @see_more_text \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 4da5f4a67..7ce8b03e2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -85,6 +85,10 @@ def detect_locale before_action :set_global_thread_values, :domain_ontology_set, :authorize_miniprofiler, :clean_empty_strings_from_params_arrays, :init_trial_license + def show_image_modal + url = params[:url] + render turbo_stream: helpers.prepend('application_modal_content') { helpers.image_tag(url, style:'width: 100%') } + end def set_global_thread_values Thread.current[:session] = session diff --git a/app/helpers/components_helper.rb b/app/helpers/components_helper.rb new file mode 100644 index 000000000..3b931cb31 --- /dev/null +++ b/app/helpers/components_helper.rb @@ -0,0 +1,80 @@ +module ComponentsHelper + def info_tooltip(text) + render Display::InfoTooltipComponent.new(text: text) + end + + def empty_state_message(message) + content_tag(:p, message.html_safe, class: 'font-italic field-description_text') + end + + def properties_list_component(c, properties, &block) + properties.each do |k, v| + c.row do + content = if block_given? + capture(v, &block) + else + v + end + render FieldContainerComponent.new(label: attr_label(k)) do + content + end + end + end + + end + + + def horizontal_list_container(values, &block) + return if Array(values).empty? + + render Layout::HorizontalListComponent.new do |l| + Array(values).each do |v| + l.element do + capture(v, &block) + end + end + end + end + + def list_container(values, &block) + return if Array(values).empty? + + render Layout::ListComponent.new do |l| + Array(values).each do |v| + l.row do + capture(v, &block) + end + end + end + end + + def properties_card(title, tooltip, properties, &block) + render Layout::CardComponent.new do |d| + d.header(text: title, tooltip: tooltip) + render(Layout::ListComponent.new) do |c| + if properties + properties_list_component(c, properties, &block) + else + capture(c, &block) + end + end + end + end + + def properties_dropdown(id, title, tooltip, properties, &block) + render DropdownContainerComponent.new(title: title, id: id, tooltip: tooltip) do |d| + d.empty_state do + properties_string = properties.keys[0..4].map{|key| "#{attr_label(key)}" }.join(', ')+'... ' if properties + empty_state_message "The fields #{properties_string} are empty" + end + + render Layout::ListComponent.new do |c| + if properties + properties_list_component(c, properties, &block) + else + capture(c, &block) + end + end + end + end +end diff --git a/app/helpers/modal_helper.rb b/app/helpers/modal_helper.rb index 068d9b7f7..568c0f866 100644 --- a/app/helpers/modal_helper.rb +++ b/app/helpers/modal_helper.rb @@ -5,7 +5,7 @@ def link_to_modal(name, options = nil, html_options = nil, &block) if name.nil? link_to(options, html_options, &block) else - link_to(name, options, html_options) + link_to(PopupLinkTextComponent.new(text: name).call, options, html_options) end end diff --git a/app/views/layouts/component_preview.html.erb b/app/views/layouts/component_preview.html.erb index f6fe9f26b..5369cd053 100644 --- a/app/views/layouts/component_preview.html.erb +++ b/app/views/layouts/component_preview.html.erb @@ -49,7 +49,7 @@ - + <%= modal_frame_container %>
<%= yield %> <%= javascript_include_tag "application" %> diff --git a/app/views/layouts/component_preview_not_centred.html.erb b/app/views/layouts/component_preview_not_centred.html.erb index b8c527da1..81b1c2964 100644 --- a/app/views/layouts/component_preview_not_centred.html.erb +++ b/app/views/layouts/component_preview_not_centred.html.erb @@ -49,8 +49,8 @@ - -<%= yield %> -<%= javascript_include_tag "application" %> + <%= modal_frame_container %> + <%= yield %> + <%= javascript_include_tag "application" %> \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 13e035de2..589464523 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -157,7 +157,7 @@ get '/ajax/:ontology/classes/:conceptid/instances' => 'instances#index_by_class', :constraints => { conceptid: /[^\/?]+/ } get '/ajax/ontologies' , to:"ontologies#ajax_ontologies" get '/ajax/agents' , to:"agents#ajax_agents" - + get '/ajax/images/show' => 'application#show_image_modal' # User get '/logout' => 'login#destroy', :as => :logout get '/lost_pass' => 'login#lost_password' diff --git a/test/components/previews/display/header_component_preview.rb b/test/components/previews/display/header_component_preview.rb new file mode 100644 index 000000000..491190896 --- /dev/null +++ b/test/components/previews/display/header_component_preview.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class Display::HeaderComponentPreview < ViewComponent::Preview + + # @param text text + # @param tooltip text + def default(text: 'header text' , tooltip: 'text tooltip') + render Display::HeaderComponent.new(text: text, tooltip: tooltip) + end +end diff --git a/test/components/previews/display/image_component_preview.rb b/test/components/previews/display/image_component_preview.rb new file mode 100644 index 000000000..d6caa1f9a --- /dev/null +++ b/test/components/previews/display/image_component_preview.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Display::ImageComponentPreview < ViewComponent::Preview + def default + render Display::ImageComponent.new(src: "empty-box.svg", title: 'Image popup') + end +end diff --git a/test/components/previews/display/info_tooltip_component_preview.rb b/test/components/previews/display/info_tooltip_component_preview.rb new file mode 100644 index 000000000..44f844972 --- /dev/null +++ b/test/components/previews/display/info_tooltip_component_preview.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class Display::InfoTooltipComponentPreview < ViewComponent::Preview + + # @param text text + def default(text: 'tooltip text') + render Display::InfoTooltipComponent.new(text: text) + end +end diff --git a/test/components/previews/layout/card_component_preview.rb b/test/components/previews/layout/card_component_preview.rb new file mode 100644 index 000000000..23f07a2d8 --- /dev/null +++ b/test/components/previews/layout/card_component_preview.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class Layout::CardComponentPreview < ViewComponent::Preview + # @param text textarea + def default(text: 'text here') + render Layout::CardComponent.new do + text.html_safe + end + end +end diff --git a/test/components/previews/layout/list_component_preview.rb b/test/components/previews/layout/list_component_preview.rb new file mode 100644 index 000000000..ea57e5836 --- /dev/null +++ b/test/components/previews/layout/list_component_preview.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class Layout::ListComponentPreview < ViewComponent::Preview + def vertical + render Layout::ListComponent.new do |l| + 4.times.each do |i| + l.row {content_tag(:div , "element #{i}", class: 'p-1 border')} + end + end + end + + def horizontal + render Layout::HorizontalListComponent.new do |l| + 4.times.each do |i| + l.element {content_tag(:div , "element #{i}", class: 'p-1 border')} + end + end + end +end