[64111] Use Primer PageHeader in repositories (#19001)

* create a helper for breadcrumbs in repositories

* move breadcrumbs to the header partial

* use page header action buttons instead of toolbar items

* use page header icon action buttons

* add page header to revisions page

* add page header to statistics page

* remove breadcrumbs in annotate, changes and entry pages

* change diff page

* change revision page and add page header component

* fix rubocup errors

* fix failing tests

* check breadcrumbs class in test

* remove test for checking the title

* fix breadcrumbs helper

* add checkout instructions toggle

* fix test for title check

* fix Assignment Branch Condition size error

* fix error while lading revision pages

* remove toolbar items classes usages

* use project id

* move page header of revision page into a component

* move page header of repository page into a component

* remove toolbar classes

* fix failing specs

* remove toolbar usages

* Re-add hook and change breadcrumb slightly

* Simplify PageHeader button template

* Replace custom input group with Primer component

* Cleanup sass code

* Correct the method for getting the "next_button" tag

---------

Co-authored-by: Henriette Darge <h.darge@openproject.com>
This commit is contained in:
Behrokh Satarnejad
2025-06-11 13:14:32 +02:00
committed by GitHub
parent d4e94ae8d0
commit 8474cd8751
20 changed files with 480 additions and 328 deletions
@@ -0,0 +1,35 @@
<%=
render Primer::OpenProject::PageHeader.new do |header|
header.with_title { t(
"repositories.named_repository",
vendor_name: @repository.class.vendor_name
) }
header.with_breadcrumbs(breadcrumb_items)
if !@empty && User.current.allowed_in_project?(:browse_repository, @project)
header.with_action_icon_button(
tag: :a,
icon: :graph,
label: t(:label_statistics),
mobile_icon: :graph,
mobile_label: t(:label_statistics),
size: :medium,
href: stats_project_repository_path(@project),
aria: { label: t(:label_statistics) },
title: t(:label_statistics)
)
end
if User.current.allowed_in_project?(:manage_repository, @project)
header.with_action_icon_button(
tag: :a,
icon: :gear,
label: t(:label_setting_plural),
mobile_icon: :gear,
mobile_label: t(:label_setting_plural),
size: :medium,
href: project_settings_repository_path(@project),
aria: { label: t(:label_setting_plural) },
title: t(:label_setting_plural)
)
end
end
%>
@@ -0,0 +1,89 @@
# frozen_string_literal: true
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module Repositories
class PageHeaderComponent < ApplicationComponent
include ApplicationHelper
include OpPrimer::ComponentHelpers
def initialize(repository:, empty: false, path: nil, rev: nil, project: nil)
super
@project = project
@repository = repository
@path = path
@rev = rev
@empty = empty
end
def breadcrumb_items
[
project_breadcrumb,
repository_breadcrumb,
*path_breadcrumbs
]
end
def project_breadcrumb
{
href: project_overview_path(@project.id),
text: @project.name
}
end
def repository_breadcrumb
{
href: url_for(action: "show", project_id: @project.id, repo_path: nil, rev: @rev),
text: t("repositories.named_repository", vendor_name: @repository.class.vendor_name)
}
end
def path_breadcrumbs
dirs = @path.to_s.split("/").compact_blank
link_path = ""
dirs.each_with_index.map do |dir, index|
link_path = File.join(link_path, dir)
if index == dirs.size - 1
dir
else
{
href: url_for(
action: "show",
project_id: @project.id,
repo_path: to_path_param(link_path),
rev: @rev
),
text: dir
}
end
end
end
end
end
@@ -0,0 +1,57 @@
<%= render Primer::OpenProject::PageHeader.new do |header| %>
<% header.with_title do %>
<%= "#{t(:label_revision)} #{helpers.format_revision(@changeset)}" %>
<% end %>
<% header.with_breadcrumbs([{
href: project_overview_path(@project.id),
text: @project.name
},
{
href: url_for({ action: "show", project_id: @project.id }),
text: t(
"repositories.named_repository",
vendor_name: @repository.class.vendor_name
)
},
{
href: revisions_project_repository_path(@project),
text: t(:label_revision_plural)
},
"#{t(:label_revision)} #{helpers.format_revision(@changeset)}"
]) %>
<%# Previous Revision Button %>
<% header.with_action_button(
tag: previous_button_tag,
icon: "arrow-left",
label: t(:label_previous),
mobile_icon: "arrow-left",
mobile_label: t(:label_previous),
href: previous_button_url,
disabled: previous_button_disabled?,
title: previous_button_title,
aria: { label: t(:label_previous) }
) do |button|
button.with_leading_visual_icon(icon: "arrow-left")
t(:label_previous)
end %>
<%# Next Revision Button %>
<% header.with_action_button(
tag: next_button_tag,
icon: "arrow-right",
label: t(:label_next),
mobile_icon: "arrow-right",
mobile_label: t(:label_next),
href: next_button_url,
disabled: next_button_disabled?,
title: next_button_title,
aria: { label: t(:label_next) }
) do |button|
button.with_leading_visual_icon(icon: "arrow-right")
t(:label_next)
end %>
<%# Revision Search Form Button (if needed) could go here too) %>
<% end %>
@@ -0,0 +1,89 @@
# frozen_string_literal: true
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module Repositories
module Revision
class PageHeaderComponent < ApplicationComponent
include ApplicationHelper
include OpPrimer::ComponentHelpers
def initialize(changeset:, repository:, project: nil)
super
@project = project
@changeset = changeset
@repository = repository
end
def previous_button_present?
@previous_button_present ||= @changeset.previous.present?
end
def next_button_present?
@next_button_present ||= @changeset.next.present?
end
def previous_button_disabled?
!previous_button_present?
end
def next_button_disabled?
!next_button_present?
end
def previous_button_url
return nil unless previous_button_present?
url_for(controller: "/repositories", action: "revision", project_id: @project, rev: @changeset.previous.identifier)
end
def next_button_url
return nil unless next_button_present?
url_for(controller: "/repositories", action: "revision", project_id: @project, rev: @changeset.next.identifier)
end
def previous_button_tag
previous_button_present? ? :a : :button
end
def next_button_tag
next_button_present? ? :a : :button
end
def previous_button_title
previous_button_present? ? t(:label_revision_id, value: helpers.format_revision(@changeset.previous)) : t(:label_previous)
end
def next_button_title
previous_button_present? ? t(:label_revision_id, value: helpers.format_revision(@changeset.next)) : t(:label_next)
end
end
end
end
+6 -6
View File
@@ -121,7 +121,7 @@ module RepositoriesHelper
style = +"change"
text = File.basename(file)
if s = tree[file][:s]
style << " folder"
style += " folder"
path_param = without_leading_slash(to_path_param(@repository.relative_path(file)))
text = link_to(h(text),
show_revisions_path_project_repository_path(project_id: @project,
@@ -129,10 +129,10 @@ module RepositoriesHelper
rev: @changeset.identifier),
title: I18n.t(:label_folder))
output << "<li class='#{style} icon icon-folder-#{calculate_folder_action(s)}'>#{text}</li>"
output << render_changes_tree(s)
output += "<li class='#{style} icon icon-folder-#{calculate_folder_action(s)}'>#{text}</li>"
output += render_changes_tree(s)
elsif c = tree[file][:c]
style << " change-#{c.action}"
style += " change-#{c.action}"
path_param = without_leading_slash(to_path_param(@repository.relative_path(c.path)))
unless c.action == "D"
@@ -156,10 +156,10 @@ module RepositoriesHelper
text << raw(" " + content_tag("span", h(c.from_path), class: "copied-from")) if c.from_path.present?
output << changes_tree_li_element(c.action, text, style)
output += changes_tree_li_element(c.action, text, style)
end
end
output << "</ul>"
output += "</ul>"
output.html_safe
end
@@ -1,60 +0,0 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) the OpenProject GmbH
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See COPYRIGHT and LICENSE files for more details.
++#%>
<%= link_to(
{ action: "show", project_id: @project, repo_path: nil, rev: @rev },
{ title: I18n.t(:label_repository_root) }
) do %>
<%= op_icon("icon-home repository-breadcrumbs--home") %>
<% end %>
<%
dirs = path.split('/')
link_path = ''
dirs.each_with_index do |dir, index|
next if dir.blank?
link_path << '/' unless link_path.empty?
link_path << "#{dir}"
%>
<span class="repository-breadcrumbs--sep"></span>
<% if index == dirs.size - 1 %>
<strong><%= h(dir) %></strong>
<% else %>
<%= link_to h(dir), action: "show", project_id: @project,
repo_path: to_path_param(link_path), rev: @rev %>
<% end %>
<% end %>
<%
# @rev is revision or git branch or tag.
rev_text = @changeset.nil? ? @rev : format_revision(@changeset)
%>
<span class="repository-bradcrumbs--identifier">
<%= "(#{t('repositories.at_identifier', identifier: rev_text)})" if rev_text.present? %>
</span>
<% html_title(h(with_leading_slash(path))) -%>
@@ -28,59 +28,55 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<opce-persistent-toggle data-identifier="repository.checkout_instructions"></opce-persistent-toggle>
<%= toolbar title: t(
"repositories.named_repository",
vendor_name: @repository.class.vendor_name
) do %>
<% if @instructions && @instructions.available? %>
<li class="toolbar-item toolbar-input-group hidden-for-mobile">
<div class="toolbar-input-group--affix -prepend">
<span><%= @instructions.checkout_command %></span>
</div>
<input id="repository-checkout-url"
type="text" class="-clickable" size="40"
value="<%= @instructions.checkout_url(true) %>"
readonly>
<% csp_onclick("this.focus(); this.select();", "#repository-checkout-url") %>
<opce-copy-to-clipboard click-target=".repository-checkout-copy-button"
clipboard-target="#repository-checkout-url">
</opce-copy-to-clipboard>
<button class="repository-checkout-copy-button toolbar-input--affix toolbar-input-group--affix -append"
title="<%= t(:button_copy_to_clipboard) %>">
<%= op_icon("icon-copy") %>
<span class="hidden-for-sighted"><%= t(:button_copy_to_clipboard) %></span>
</button>
</li>
<li class="toolbar-item -icon-only">
<a id="repository--checkout-instructions-toggle"
class="persistent-toggle--click-handler button"
title="<%= t("repositories.checkout.show_instructions") %>">
<%= op_icon("button--icon icon-info1") %>
</a>
</li>
<% end %>
<% if !empty && User.current.allowed_in_project?(:browse_repository, @project) %>
<li class="toolbar-item -icon-only">
<%= link_to stats_project_repository_path(@project),
class: "button", title: t(:label_statistics) do %>
<%= op_icon("button--icon icon-chart1") %>
<% end %>
</li>
<% end %>
<%= call_hook(
:repositories_navigation_toolbar,
{ repository: @repository, project: @project, repository_empty: empty }
) %>
<% if User.current.allowed_in_project?(:manage_repository, @project) %>
<li class="toolbar-item -icon-only">
<%= link_to project_settings_repository_path(@project),
class: "button", title: t(:label_setting_plural) do %>
<%= op_icon("button--icon icon-settings") %>
<% end %>
</li>
<% end %>
<%= render(Repositories::PageHeaderComponent.new(repository: @repository, empty: empty, path: @path, rev: @rev, project: @project)) %>
<% if @instructions && @instructions.available? %>
<%=
render(Primer::OpenProject::FlexLayout.new(align_items: :flex_end, mb: 2)) do |container|
container.with_column(mr: 2, classes: "repository--checkout-container") do
render(Primer::OpenProject::InputGroup.new(input_width: :large)) do |group|
group.with_text_input(
id: "repository-checkout-url",
name: "repository-checkout-url",
label: @instructions.checkout_command,
value: @instructions.checkout_url(true),
readonly: true
)
group.with_trailing_action_clipboard_copy_button(
value: @instructions.checkout_url(true),
aria: { label: t(:button_copy_to_clipboard) }
)
end
end
container.with_column do
render(
Primer::Beta::IconButton.new(
icon: :info,
tag: :a,
id: "repository--checkout-instructions-toggle",
classes: "persistent-toggle--click-handler",
aria: { label: t("repositories.checkout.show_instructions") },
title: t("repositories.checkout.show_instructions")
)
)
end
end
%>
<% end %>
<%= call_hook(
:repositories_navigation_toolbar,
{ repository: @repository, project: @project, repository_empty: empty }
) %>
<%
# @rev is revision or git branch or tag.
rev_text = @changeset.nil? ? @rev : format_revision(@changeset)
%>
<span>
<%= "(#{t('repositories.at_identifier', identifier: rev_text)})" if rev_text.present? %>
</span>
<% if @instructions %>
<%= render partial: "checkout_instructions",
locals: { repository: @repository, instructions: @instructions } %>
-4
View File
@@ -30,10 +30,6 @@ See COPYRIGHT and LICENSE files for more details.
<% html_title(t(:button_annotate)) %>
<%= render partial: "repository_header", locals: { empty: false } %>
<div class="repository-breadcrumbs">
<%= render partial: "breadcrumbs",
locals: { path: @path, revision: @rev }.merge(kind: "file") %>
</div>
<p><%= render partial: "link_to_functions" %></p>
<% if @annotate.nil? || @annotate.empty? %>
-5
View File
@@ -30,11 +30,6 @@ See COPYRIGHT and LICENSE files for more details.
<%= call_hook(:view_repositories_show_contextual, { repository: @repository, project: @project }) %>
<%= render partial: "repository_header", locals: { empty: false } %>
<div class="repository-breadcrumbs">
<%= render partial: "breadcrumbs",
locals: { path: @path, revision: @rev }.merge(kind: (@entry ? @entry.kind : nil)) %>
</div>
<p><%= render partial: "link_to_functions" %></p>
<%= render_properties(@properties) %>
+34 -15
View File
@@ -27,23 +27,42 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<%= toolbar title: "#{t(:label_revision)} #{@diff_format_revisions} #{@path}" do %>
<li class="toolbar-item">
<%= form_tag({repo_path: to_path_param(@path)}, method: :get) do %>
<%= hidden_field_tag('rev', params[:rev]) if params[:rev] %>
<%= hidden_field_tag('rev_to', params[:rev_to]) if params[:rev_to] %>
<%= styled_select_tag 'type', options_for_select([[t(:label_diff_inline), "inline"], [t(:label_diff_side_by_side), "sbs"]], @diff_type), id: "repository-diff-type-select" %>
<% end %>
<%=
content_for(:additional_js_dom_ready) do
"jQuery('#repository-diff-type-select').change(function() {
<%=
render Primer::OpenProject::PageHeader.new do |header|
header.with_title { "#{t(:label_revision)} #{@diff_format_revisions} #{@path}" }
header.with_breadcrumbs([{
href: project_overview_path(@project.id),
text: @project.name
},
{
href: url_for({ action: "show", project_id: @project.id }),
text: t(
"repositories.named_repository",
vendor_name: @repository.class.vendor_name
)
},
{
href: revisions_project_repository_path(@project),
text: t(:label_revision_plural)
},
"#{t(:label_revision)} #{@diff_format_revisions} #{@path}"
])
end
%>
<div class="repository-input-group">
<%= form_tag({repo_path: to_path_param(@path)}, method: :get) do %>
<%= hidden_field_tag('rev', params[:rev]) if params[:rev] %>
<%= hidden_field_tag('rev_to', params[:rev_to]) if params[:rev_to] %>
<%= styled_select_tag 'type', options_for_select([[t(:label_diff_inline), "inline"], [t(:label_diff_side_by_side), "sbs"]], @diff_type), id: "repository-diff-type-select" %>
<% end %>
<%=
content_for(:additional_js_dom_ready) do
"jQuery('#repository-diff-type-select').change(function() {
if (this.value != '') { this.form.submit() }
});".html_safe
end
%>
</li>
<% end %>
end
%>
</div>
<% cache(@cache_key) do -%>
<%= render partial: 'common/diff', locals: { diff: @diff, diff_type: @diff_type } %>
<% end -%>
-5
View File
@@ -31,11 +31,6 @@ See COPYRIGHT and LICENSE files for more details.
<%= render partial: "repository_header", locals: { empty: false } %>
<div class="repository-breadcrumbs">
<%= render partial: "breadcrumbs",
locals: { path: @path, revision: @rev }.merge(kind: "dir") %>
</div>
<p><%= render partial: "link_to_functions" %></p>
<%= render partial: "common/file", locals: { filename: @path, content: @content } %>
+3 -20
View File
@@ -27,26 +27,9 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<%= toolbar title: "#{t(:label_revision)} #{format_revision(@changeset)}" do %>
<li class="toolbar-item">
<% if @changeset.previous.nil? %>
<button class="button" disabled="disabled"><%= t(:label_previous) %></button>
<% else %>
<%= link_to_revision(@changeset.previous, @project, text: t(:label_previous), class: "button") %>
<% end %>
</li>
<li class="toolbar-item">
<% if @changeset.next.nil? %>
<button class="button" disabled="disabled"><%= t(:label_next) %></button>
<% else %>
<%= link_to_revision(@changeset.next, @project, text: t(:label_next), class: "button") %>
<% end %>
</li>
<li class="toolbar-item">
<%= form_tag({ controller: "/repositories", action: "revision", project_id: @project }, method: :get) do %>
<%= text_field_tag :rev, @rev, placeholder: t(:label_revision) %>
<% end %>
</li>
<%= render(Repositories::Revision::PageHeaderComponent.new(changeset: @changeset, repository: @repository, project: @project)) %>
<%= form_tag({ controller: "/repositories", action: "revision", project_id: @project }, method: :get) do %>
<%= text_field_tag :rev, @rev, placeholder: t(:label_revision) %>
<% end %>
<p><% if @changeset.scmid %>ID: <%= h(@changeset.scmid) %><br>
<% end %>
+26 -10
View File
@@ -26,16 +26,32 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See COPYRIGHT and LICENSE files for more details.
++#%>
<%= toolbar title: t(:label_revision_plural) do %>
<%= form_tag({ action: "revision", id: @project }, { method: :get }) do %>
<li class="toolbar-item">
<%= label_tag :rev, t(:label_revision), class: "hidden-for-sighted" %>
<%= text_field_tag :rev, @rev, size: 8, placeholder: t(:label_revision) %>
</li>
<li class="toolbar-item">
<%= submit_tag "OK", class: "button -primary" %>
</li>
<% end %>
<%=
render Primer::OpenProject::PageHeader.new do |header|
header.with_title { t(:label_revision_plural) }
header.with_breadcrumbs([{
href: project_overview_path(@project.id),
text: @project.name
},
{
href: url_for({ action: "show", project_id: @project.id }),
text: t(
"repositories.named_repository",
vendor_name: @repository.class.vendor_name
)
},
t(:label_revision_plural)
])
end
%>
<%= form_tag({ action: "revision", id: @project }, { method: :get }) do %>
<div class="revisions-action-container">
<%= label_tag :rev, t(:label_revision), class: "hidden-for-sighted" %>
<%= text_field_tag :rev, @rev, size: 8, placeholder: t(:label_revision) %>
<%= submit_tag "OK", class: "button -primary" %>
</div>
<% end %>
<%= render partial: "revisions", locals: { project: @project, path: "", revisions: @changesets, entry: nil } %>
<%= pagination_links_full @changesets %>
+55 -54
View File
@@ -30,19 +30,15 @@ See COPYRIGHT and LICENSE files for more details.
<% content_controller "repository-navigation", dynamic: true %>
<%= render partial: "repository_header", locals: { empty: false } %>
<div class="repository-breadcrumbs">
<%= render partial: "breadcrumbs",
locals: { path: @path, revision: @rev }.merge(kind: "dir") %>
</div>
<% if !@entries.nil? && authorize_for('repositories', 'browse') %>
<%= render partial: "dir_list" %>
<% end %>
<%= render_properties(@properties) %>
<%= toolbar title: t(:label_revision_plural), html: { class: "repository--revision-toolbar" } do %>
<div class="repository--revision-toolbar">
<%# rev => nil prevents overwriting the rev parameter queried for in the form with the parameter from the request %>
<div class="revisions-title-container"><h2><%= t(:label_revision_plural) %></h2></div>
<%= form_tag(
{ action: controller.action_name,
project_id: @project,
@@ -55,60 +51,65 @@ See COPYRIGHT and LICENSE files for more details.
"repository-navigation-target": "form"
}
) do %>
<li class="toolbar-item toolbar-input-group hidden-for-mobile">
<div>
<%= label_tag "rev", I18n.t("repositories.go_to_revision") %>
<div class="revisions-items">
<div class="revisions-item hidden-for-mobile">
<div class="revisions-item--label">
<%= label_tag "rev", I18n.t("repositories.go_to_revision") %>
</div>
<%= text_field_tag :rev,
@rev,
id: "revision-identifier-input",
class: "revisions-item--input",
placeholder: t(:label_revision),
data: {
"repository-navigation-target": "revision",
action: "keydown.enter->repository-navigation#sendForm"
} %>
</div>
<%= text_field_tag :rev,
@rev,
id: "revision-identifier-input",
placeholder: t(:label_revision),
<% if !@repository.branches.nil? && @repository.branches.length > 0 %>
<div class="revisions-item hidden-for-mobile">
<div class="revisions-item--label">
<%= label_tag "branch", I18n.t(:label_branch) %>
</div>
<%= select_tag :branch,
options_for_select(@repository.branches, @rev),
include_blank: "--- #{t(:actionview_instancetag_blank_option)} ---",
id: "revision-branch-select",
class: "revisions-item--input",
data: {
"repository-navigation-target": "revision",
action: "keydown.enter->repository-navigation#sendForm"
"repository-navigation-target": "branch",
action: "repository-navigation#applyValue"
} %>
</li>
<% if !@repository.branches.nil? && @repository.branches.length > 0 %>
<li class="toolbar-item toolbar-input-group hidden-for-mobile">
<div>
<%= label_tag "branch", I18n.t(:label_branch) %>
</div>
<%= select_tag :branch,
options_for_select(@repository.branches, @rev),
include_blank: "--- #{t(:actionview_instancetag_blank_option)} ---",
id: "revision-branch-select",
data: {
"repository-navigation-target": "branch",
action: "repository-navigation#applyValue"
} %>
</li>
<% end %>
<% if !@repository.tags.nil? && @repository.tags.length > 0 %>
<li class="toolbar-item toolbar-input-group hidden-for-mobile">
<div>
<%= label_tag "tag", I18n.t(:label_tag) %>
</div>
<%= select_tag :tag,
options_for_select(@repository.tags, @rev),
include_blank: "--- #{t(:actionview_instancetag_blank_option)} ---",
id: "revision-tag-select",
data: {
"repository-navigation-target": "tag",
action: "repository-navigation#applyValue"
} %>
</li>
<% end %>
<li class="toolbar-item -icon-only">
<%= link_to(
{ url: { action: "revision#revision-identifier-inputs", project_id: @project,
key: User.current.rss_key } },
{ class: "button", title: t("repositories.atom_revision_feed") }
) do %>
<%= op_icon("button--icon icon-export-atom") %>
<% end %>
</li>
<% if !@repository.tags.nil? && @repository.tags.length > 0 %>
<div class="revisions-item hidden-for-mobile">
<div class="revisions-item--label">
<%= label_tag "tag", I18n.t(:label_tag) %>
</div>
<%= select_tag :tag,
options_for_select(@repository.tags, @rev),
include_blank: "--- #{t(:actionview_instancetag_blank_option)} ---",
id: "revision-tag-select",
class: "revisions-item--input",
data: {
"repository-navigation-target": "tag",
action: "repository-navigation#applyValue"
} %>
</div>
<% end %>
<div class="revisions-item -icon-only">
<%= link_to(
{ url: { action: "revision#revision-identifier-inputs", project_id: @project,
key: User.current.rss_key } },
{ class: "button button_no-margin", title: t("repositories.atom_revision_feed") }
) do %>
<%= op_icon("button--icon icon-export-atom") %>
<% end %>
</div>
</div>
<% end %>
<% end %>
</div>
<% if authorize_for('repositories', 'revisions') %>
<% if @changesets && !@changesets.empty? %>
+10 -1
View File
@@ -27,7 +27,16 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<%= toolbar title: t(:label_statistics) %>
<%=
render Primer::OpenProject::PageHeader.new do |header|
header.with_title { t(:label_statistics) }
header.with_breadcrumbs([{ href: project_overview_path(@project.id), text: @project.name },
{ href: url_for({ action: "show", project_id: @project }),
text: @repository ? t("repositories.named_repository", vendor_name: @repository.class.vendor_name) : t(:label_repository) },
t(:label_statistics)
])
end
%>
<p class="autoscroll">
<%= tag(
-2
View File
@@ -191,7 +191,6 @@ import { NoResultsComponent } from 'core-app/shared/components/no-results/no-res
import {
OpNonWorkingDaysListComponent,
} from 'core-app/shared/components/op-non-working-days-list/op-non-working-days-list.component';
import { CopyToClipboardComponent } from 'core-app/shared/components/copy-to-clipboard/copy-to-clipboard.component';
import { GlobalSearchTitleComponent } from 'core-app/core/global_search/title/global-search-title.component';
import { PersistentToggleComponent } from 'core-app/shared/components/persistent-toggle/persistent-toggle.component';
import { TypeFormConfigurationComponent } from 'core-app/features/admin/types/type-form-configuration.component';
@@ -453,7 +452,6 @@ export class OpenProjectModule implements DoBootstrap {
registerCustomElement('opce-non-working-days-list', OpNonWorkingDaysListComponent, { injector });
registerCustomElement('opce-main-menu-toggle', MainMenuToggleComponent, { injector });
registerCustomElement('opce-main-menu-resizer', MainMenuResizerComponent, { injector });
registerCustomElement('opce-copy-to-clipboard', CopyToClipboardComponent, { injector });
registerCustomElement('opce-global-search-title', GlobalSearchTitleComponent, { injector });
registerCustomElement('opce-persistent-toggle', PersistentToggleComponent, { injector });
registerCustomElement('opce-admin-type-form-configuration', TypeFormConfigurationComponent, { injector });
@@ -1,74 +0,0 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) the OpenProject GmbH
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See COPYRIGHT and LICENSE files for more details.
//++
import { ChangeDetectionStrategy, Component, ElementRef, OnInit } from '@angular/core';
import { ToastService } from 'core-app/shared/components/toaster/toast.service';
import { I18nService } from 'core-app/core/i18n/i18n.service';
import { CopyToClipboardService } from './copy-to-clipboard.service';
@Component({
template: '',
selector: 'opce-copy-to-clipboard',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CopyToClipboardComponent implements OnInit {
public clickTarget:string;
public clipboardTarget:string;
private target:JQuery;
constructor(
readonly toastService:ToastService,
readonly elementRef:ElementRef,
readonly I18n:I18nService,
protected copyToClipboardService:CopyToClipboardService,
) {
}
ngOnInit() {
const element = this.elementRef.nativeElement;
// Get inputs as attributes since this is a bootstrapped directive
this.clickTarget = element.getAttribute('click-target');
this.clipboardTarget = element.getAttribute('clipboard-target');
jQuery(this.clickTarget).on('click', (evt:JQuery.TriggeredEvent) => this.onClick(evt));
element.classList.add('copy-to-clipboard');
this.target = jQuery(this.clipboardTarget ? this.clipboardTarget : element);
}
onClick($event:JQuery.TriggeredEvent) {
$event.preventDefault();
// Select the text in case the clipboard is not supported by the browser
this.target.select().focus();
this.copyToClipboardService.copy(String(this.target.val()));
}
}
-4
View File
@@ -62,7 +62,6 @@ import { HomescreenNewFeaturesBlockComponent } from 'core-app/features/homescree
import { TablePaginationComponent } from 'core-app/shared/components/table-pagination/table-pagination.component';
import { StaticQueriesService } from 'core-app/shared/components/op-view-select/op-static-queries.service';
import { CopyToClipboardService } from './components/copy-to-clipboard/copy-to-clipboard.service';
import { CopyToClipboardComponent } from './components/copy-to-clipboard/copy-to-clipboard.component';
import { OpDateTimeComponent } from './components/date/op-date-time.component';
import { ToastComponent } from './components/toaster/toast.component';
import { ToastsContainerComponent } from './components/toaster/toasts-container.component';
@@ -197,9 +196,6 @@ export function bootstrapModule(injector:Injector):void {
OPContextMenuComponent,
IconTriggeredContextMenuComponent,
// Add functionality to rails rendered templates
CopyToClipboardComponent,
ResizerComponent,
TablePaginationComponent,
@@ -71,29 +71,41 @@ li.change
margin-right: 1em
.repository--revision-toolbar
display: flex
margin-top: 3rem
.repository--checkout-instructions--url
margin-top: 1rem
.repository--checkout-container
width: 450px
.repository-breadcrumbs
font-size: 0.9rem
margin: 15px 0
.repository-input-group
display: flex
.repository-breadcrumbs--home
font-size: 0.75rem
.revisions-action-container
display: flex
gap: 0.5rem
width: 50%
.button
height: 36px
.repository-bradcrumbs--identifier
display: inline-block
margin-left: 10px
.revisions-title-container
flex: 1 1
white-space: nowrap
max-width: 100%
.repository-breadcrumbs--sep
display: inline-block
margin: 0 2px
.revisions-items
display: flex
gap: 0.5rem
&::before
content: ''
color: var(--fgColor-muted)
.revisions-item
display: flex
align-items: center
.revisions-item--input
flex: 1
flex-basis: 150px
.revisions-item--label
padding: 0 5px
table.filecontent
border: 1px solid var(--borderColor-default)
@@ -244,7 +244,7 @@ RSpec.describe RepositoriesController do
shared_examples "renders the repository title" do |active_breadcrumb|
it do
expect(response).to be_successful
expect(response.body).to have_css(".repository-breadcrumbs", text: active_breadcrumb)
expect(response.body).to have_css(".PageHeader-breadcrumbs", text: active_breadcrumb)
end
end