Skip to content
Snippets Groups Projects
Commit 75496059 authored by Dennis Tang's avatar Dennis Tang 🎨
Browse files

Further design iteration on project overview

Continues the iteration on the project overview UI:

- moved star, fork and new clone button
(copy SSH/HTTPS URLs) to top right,
  made them smaller
- avatar is now larger (64px)
- 'Request access' is now a link instead
  of a button
- overview comes before the description +
  changed styling and added icons
- description font-size is now 16px (large-paragraph)
- quick links to files are moved downwards
  below the commit/pipeline info
- margins changed to group content into
  4 groups to clean up the interface
- visibility info reduced to icon-only and
  moved to the right of the title
parent 61d91f64
No related branches found
No related tags found
No related merge requests found
Showing
with 403 additions and 201 deletions
Loading
Loading
@@ -12,6 +12,10 @@ export default function notificationsDropdown() {
const form = $(this).parents('.notification-form:first');
 
form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner');
if (form.hasClass('no-label')) {
form.find('.js-notification-loading').toggleClass('hidden');
form.find('.js-notifications-icon').toggleClass('hidden');
}
form.find('#notification_setting_level').val(notificationLevel);
form.submit();
});
Loading
Loading
Loading
Loading
@@ -13,6 +13,9 @@ export default class Project {
const $cloneOptions = $('ul.clone-options-dropdown');
const $projectCloneField = $('#project_clone');
const $cloneBtnLabel = $('.js-git-clone-holder .js-clone-dropdown-label');
const mobileCloneField = document.querySelector(
'.js-mobile-git-clone .js-clone-dropdown-label',
);
 
const selectedCloneOption = $cloneBtnLabel.text().trim();
if (selectedCloneOption.length > 0) {
Loading
Loading
@@ -36,7 +39,11 @@ export default class Project {
$label.text(activeText);
});
 
$projectCloneField.val(url);
if (mobileCloneField) {
mobileCloneField.dataset.clipboardText = url;
} else {
$projectCloneField.val(url);
}
$('.js-git-empty .js-clone').text(url);
});
// Ref switcher
Loading
Loading
Loading
Loading
@@ -21,6 +21,7 @@
&.s46 { @include avatar-size(46px, 15px); }
&.s48 { @include avatar-size(48px, 10px); }
&.s60 { @include avatar-size(60px, 12px); }
&.s64 { @include avatar-size(64px, 14px); }
&.s70 { @include avatar-size(70px, 14px); }
&.s90 { @include avatar-size(90px, 15px); }
&.s100 { @include avatar-size(100px, 15px); }
Loading
Loading
@@ -80,6 +81,7 @@
&.s40 { font-size: 16px; line-height: 38px; }
&.s48 { font-size: 20px; line-height: 46px; }
&.s60 { font-size: 32px; line-height: 58px; }
&.s64 { font-size: 32px; line-height: 64px; }
&.s70 { font-size: 34px; line-height: 70px; }
&.s90 { font-size: 36px; line-height: 88px; }
&.s100 { font-size: 36px; line-height: 98px; }
Loading
Loading
Loading
Loading
@@ -142,8 +142,14 @@
 
&.btn-sm {
padding: 4px 10px;
font-size: 13px;
line-height: 18px;
font-size: $gl-btn-small-font-size;
line-height: $gl-btn-small-line-height;
}
&.btn-xs {
padding: 2px $gl-btn-padding;
font-size: $gl-btn-small-font-size;
line-height: $gl-btn-small-line-height;
}
 
&.btn-success,
Loading
Loading
Loading
Loading
@@ -39,15 +39,6 @@
.git-clone-holder {
display: none;
}
// Display Star and Fork buttons without counters on mobile.
.project-repo-buttons {
display: block;
.count-buttons .count-badge {
margin-top: $gl-padding-8;
}
}
}
 
.group-buttons {
Loading
Loading
Loading
Loading
@@ -197,6 +197,7 @@ $well-light-text-color: #5b6169;
$gl-font-size: 14px;
$gl-font-size-xs: 11px;
$gl-font-size-small: 12px;
$gl-font-size-large: 16px;
$gl-font-weight-normal: 400;
$gl-font-weight-bold: 600;
$gl-text-color: #2e2e2e;
Loading
Loading
@@ -270,7 +271,8 @@ $performance-bar-height: 35px;
$flash-height: 52px;
$context-header-height: 60px;
$breadcrumb-min-height: 48px;
$project-title-row-height: 24px;
$project-title-row-height: 64px;
$project-avatar-mobile-size: 24px;
$gl-line-height: 16px;
$gl-line-height-24: 24px;
 
Loading
Loading
@@ -365,6 +367,8 @@ $gl-btn-padding: 10px;
$gl-btn-line-height: 16px;
$gl-btn-vert-padding: 8px;
$gl-btn-horz-padding: 12px;
$gl-btn-small-font-size: 13px;
$gl-btn-small-line-height: 13px;
 
/*
* Badges
Loading
Loading
Loading
Loading
@@ -144,7 +144,6 @@
.group-home-panel {
padding-top: 24px;
padding-bottom: 24px;
border-bottom: 1px solid $border-color;
 
.group-avatar {
float: none;
Loading
Loading
@@ -155,7 +154,6 @@
}
}
 
.project-title,
.group-title {
margin-top: 10px;
margin-bottom: 10px;
Loading
Loading
@@ -195,25 +193,69 @@
}
 
.project-home-panel {
padding-top: $gl-padding-8;
padding-bottom: $gl-padding-24;
.project-title-row {
margin-right: $gl-padding-8;
}
padding-top: $gl-padding;
padding-bottom: $gl-padding;
 
.project-avatar {
width: $project-title-row-height;
height: $project-title-row-height;
flex-shrink: 0;
flex-basis: $project-title-row-height;
margin: 0 $gl-padding-8 0 0;
margin: 0 $gl-padding 0 0;
}
 
.project-title {
margin-top: 8px;
margin-bottom: 5px;
font-size: 20px;
line-height: $project-title-row-height;
line-height: $gl-line-height-24;
font-weight: bold;
.icon {
font-size: $gl-font-size-large;
}
.project-visibility {
color: $gl-text-color-secondary;
}
.project-tag-list {
font-size: $gl-font-size;
font-weight: $gl-font-weight-normal;
.icon {
position: relative;
top: 3px;
margin-right: $gl-padding-4;
}
}
}
.project-title-row {
@include media-breakpoint-down(sm) {
.project-avatar {
width: $project-avatar-mobile-size;
height: $project-avatar-mobile-size;
flex-basis: $project-avatar-mobile-size;
.avatar {
font-size: 20px;
line-height: 46px;
}
}
.project-title {
margin-top: 4px;
margin-bottom: 2px;
font-size: $gl-font-size;
line-height: $gl-font-size-large;
}
.project-tag-list,
.project-metadata {
font-size: $gl-font-size-small;
}
}
}
 
.project-metadata {
Loading
Loading
@@ -222,16 +264,6 @@
line-height: $gl-btn-line-height;
color: $gl-text-color-secondary;
 
.icon {
margin-right: $gl-padding-4;
font-size: 16px;
}
.project-visibility,
.project-license,
.project-tag-list {
margin-right: $gl-padding-8;
}
 
.project-license {
.btn {
Loading
Loading
@@ -240,12 +272,22 @@
}
}
 
.project-tag-list,
.project-license {
.icon {
position: relative;
top: 2px;
}
.access-request-link,
.project-tag-list {
padding-left: $gl-padding-8;
border-left: 1px solid $gl-text-color-secondary;
}
}
.project-description {
@include media-breakpoint-up(md) {
font-size: $gl-font-size-large;
}
}
.notifications-btn {
.fa-bell {
margin-right: 0;
}
}
}
Loading
Loading
@@ -298,14 +340,6 @@
vertical-align: top;
margin-top: $gl-padding;
 
.count-badge {
height: $input-height;
.icon {
top: -1px;
}
}
.count-badge-count,
.count-badge-button {
border: 1px solid $border-color;
Loading
Loading
@@ -319,29 +353,25 @@
 
.count-badge-count {
padding: 0 12px;
border-right: 0;
border-radius: $border-radius-base 0 0 $border-radius-base;
background: $gray-light;
border-radius: 0 $border-radius-base $border-radius-base 0;
}
 
.count-badge-button {
border-radius: 0 $border-radius-base $border-radius-base 0;
border-right: 0;
border-radius: $border-radius-base 0 0 $border-radius-base;
}
}
 
.project-clone-holder {
display: inline-block;
margin: $gl-padding $gl-padding-8 0 0;
margin: $gl-padding 0 0;
 
input {
height: $input-height;
}
}
 
.clone-dropdown-btn {
background-color: $white-light;
}
.clone-options-dropdown {
min-width: 240px;
 
Loading
Loading
@@ -355,6 +385,31 @@
}
}
 
.project-repo-buttons {
.icon {
top: 0;
}
.count-badge,
.btn-xs {
height: 24px;
}
.dropdown-toggle,
.clone-dropdown-btn {
.fa {
color: unset;
}
}
.btn {
.notifications-icon {
top: 1px;
margin-right: 0;
}
}
}
.split-one {
display: inline-table;
margin-right: 12px;
Loading
Loading
@@ -715,10 +770,10 @@
border-bottom: 1px solid $border-color;
}
 
.project-stats {
.project-stats,
.project-buttons {
font-size: 0;
text-align: center;
border-bottom: 1px solid $border-color;
 
.scrolling-tabs-container {
.scrolling-tabs {
Loading
Loading
@@ -786,23 +841,43 @@
font-size: $gl-font-size;
line-height: $gl-btn-line-height;
color: $gl-text-color-secondary;
white-space: nowrap;
white-space: pre-wrap;
}
 
.stat-link {
border-bottom: 0;
color: $black;
 
&:hover,
&:focus {
color: $gl-text-color;
text-decoration: underline;
border-bottom: 0;
}
.project-stat-value {
color: $gl-text-color;
}
.icon {
color: $gl-text-color-secondary;
}
.add-license-link {
&,
.icon {
color: $blue-600;
}
}
}
 
.btn {
padding: $gl-btn-vert-padding $gl-btn-horz-padding;
margin-top: $gl-padding;
padding: $gl-btn-vert-padding $gl-btn-padding;
line-height: $gl-btn-line-height;
.icon {
top: 0;
}
}
 
.btn-missing {
Loading
Loading
@@ -811,6 +886,13 @@
}
}
 
.project-buttons {
.stat-text {
@extend .btn;
@extend .btn-default;
}
}
.repository-languages-bar {
height: 8px;
margin-bottom: $gl-padding-8;
Loading
Loading
@@ -934,8 +1016,6 @@ pre.light-well {
}
 
.git-clone-holder {
width: 320px;
.btn-clipboard {
border: 1px solid $border-color;
}
Loading
Loading
@@ -958,6 +1038,15 @@ pre.light-well {
}
}
 
.git-clone-holder,
.mobile-git-clone {
.btn {
.icon {
fill: $white;
}
}
}
.cannot-be-merged,
.cannot-be-merged:hover {
color: $red-500;
Loading
Loading
Loading
Loading
@@ -16,7 +16,11 @@ class NotificationSettingsController < ApplicationController
@notification_setting = current_user.notification_settings.find(params[:id])
@saved = @notification_setting.update(notification_setting_params_for(@notification_setting.source))
 
render_response
if params[:hide_label].present?
render_response("projects/buttons/_notifications")
else
render_response
end
end
 
private
Loading
Loading
@@ -37,9 +41,9 @@ class NotificationSettingsController < ApplicationController
can?(current_user, ability_name, resource)
end
 
def render_response
def render_response(response_template = "shared/notifications/_button")
render json: {
html: view_to_html_string("shared/notifications/_button", notification_setting: @notification_setting),
html: view_to_html_string(response_template, notification_setting: @notification_setting),
saved: @saved
}
end
Loading
Loading
Loading
Loading
@@ -85,13 +85,14 @@ module ButtonHelper
dropdown_item_with_description('SSH', dropdown_description, href: append_url, data: { clone_type: 'ssh' })
end
 
def dropdown_item_with_description(title, description, href: nil, data: nil)
def dropdown_item_with_description(title, description, href: nil, data: nil, default: false)
active_class = "is-active" if default
button_content = content_tag(:strong, title, class: 'dropdown-menu-inner-title')
button_content << content_tag(:span, description, class: 'dropdown-menu-inner-content') if description
 
content_tag (href ? :a : :span),
(href ? button_content : title),
class: "#{title.downcase}-selector",
class: "#{title.downcase}-selector #{active_class}",
href: (href if href),
data: (data if data)
end
Loading
Loading
Loading
Loading
@@ -140,7 +140,7 @@ module VisibilityLevelHelper
end
 
def project_visibility_icon_description(level)
"#{project_visibility_level_description(level)}"
"#{visibility_level_label(level)} - #{project_visibility_level_description(level)}"
end
 
def visibility_level_label(level)
Loading
Loading
Loading
Loading
@@ -6,27 +6,27 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
include GitlabRoutingHelper
include StorageHelper
include TreeHelper
include IconsHelper
include ChecksCollaboration
include Gitlab::Utils::StrongMemoize
 
presents :project
 
AnchorData = Struct.new(:enabled, :label, :link, :class_modifier)
AnchorData = Struct.new(:is_link, :label, :link, :class_modifier, :icon)
MAX_TAGS_TO_SHOW = 3
 
def statistic_icon(icon_name = 'plus-square-o')
sprite_icon(icon_name, size: 16, css_class: 'icon append-right-4')
end
def statistics_anchors(show_auto_devops_callout:)
[
readme_anchor_data,
changelog_anchor_data,
contribution_guide_anchor_data,
files_anchor_data,
license_anchor_data,
commits_anchor_data,
branches_anchor_data,
tags_anchor_data,
gitlab_ci_anchor_data,
autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout),
kubernetes_cluster_anchor_data
].compact.select { |item| item.enabled }
files_anchor_data
].compact.select(&:is_link)
end
 
def statistics_buttons(show_auto_devops_callout:)
Loading
Loading
@@ -37,27 +37,28 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout),
kubernetes_cluster_anchor_data,
gitlab_ci_anchor_data
].compact.reject { |item| item.enabled }
].compact.reject(&:is_link)
end
 
def empty_repo_statistics_anchors
[
files_anchor_data,
license_anchor_data,
commits_anchor_data,
branches_anchor_data,
tags_anchor_data,
autodevops_anchor_data,
kubernetes_cluster_anchor_data
].compact.select { |item| item.enabled }
files_anchor_data
].compact.select { |item| item.is_link }
end
 
def empty_repo_statistics_buttons
[
new_file_anchor_data,
readme_anchor_data,
changelog_anchor_data,
contribution_guide_anchor_data,
autodevops_anchor_data,
kubernetes_cluster_anchor_data
].compact.reject { |item| item.enabled }
].compact.reject { |item| item.is_link }
end
 
def default_view
Loading
Loading
@@ -113,7 +114,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
 
def add_contribution_guide_path
add_special_file_path(file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide')
add_special_file_path(file_name: 'CONTRIBUTING.md', commit_message: 'Add CONTRIBUTING')
end
 
def add_ci_yml_path
Loading
Loading
@@ -149,32 +150,52 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
 
def files_anchor_data
AnchorData.new(true,
_('Files (%{human_size})') % { human_size: storage_counter(statistics.total_repository_size) },
statistic_icon('doc-code') +
_('%{strong_start}%{human_size}%{strong_end} Files').html_safe % {
human_size: storage_counter(statistics.total_repository_size),
strong_start: '<strong class="project-stat-value">'.html_safe,
strong_end: '</strong>'.html_safe
},
empty_repo? ? nil : project_tree_path(project))
end
 
def commits_anchor_data
AnchorData.new(true,
n_('Commit (%{commit_count})', 'Commits (%{commit_count})', statistics.commit_count) % { commit_count: number_with_delimiter(statistics.commit_count) },
statistic_icon('commit') +
n_('%{strong_start}%{commit_count}%{strong_end} Commit', '%{strong_start}%{commit_count}%{strong_end} Commits', statistics.commit_count).html_safe % {
commit_count: number_with_delimiter(statistics.commit_count),
strong_start: '<strong class="project-stat-value">'.html_safe,
strong_end: '</strong>'.html_safe
},
empty_repo? ? nil : project_commits_path(project, repository.root_ref))
end
 
def branches_anchor_data
AnchorData.new(true,
n_('Branch (%{branch_count})', 'Branches (%{branch_count})', repository.branch_count) % { branch_count: number_with_delimiter(repository.branch_count) },
statistic_icon('branch') +
n_('%{strong_start}%{branch_count}%{strong_end} Branch', '%{strong_start}%{branch_count}%{strong_end} Branches', repository.branch_count).html_safe % {
branch_count: number_with_delimiter(repository.branch_count),
strong_start: '<strong class="project-stat-value">'.html_safe,
strong_end: '</strong>'.html_safe
},
empty_repo? ? nil : project_branches_path(project))
end
 
def tags_anchor_data
AnchorData.new(true,
n_('Tag (%{tag_count})', 'Tags (%{tag_count})', repository.tag_count) % { tag_count: number_with_delimiter(repository.tag_count) },
statistic_icon('label') +
n_('%{strong_start}%{tag_count}%{strong_end} Tag', '%{strong_start}%{tag_count}%{strong_end} Tags', repository.tag_count).html_safe % {
tag_count: number_with_delimiter(repository.tag_count),
strong_start: '<strong class="project-stat-value">'.html_safe,
strong_end: '</strong>'.html_safe
},
empty_repo? ? nil : project_tags_path(project))
end
 
def new_file_anchor_data
if current_user && can_current_user_push_to_default_branch?
AnchorData.new(false,
_('New file'),
statistic_icon + _('New file'),
project_new_blob_path(project, default_branch || 'master'),
'success')
end
Loading
Loading
@@ -183,40 +204,45 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def readme_anchor_data
if current_user && can_current_user_push_to_default_branch? && repository.readme.nil?
AnchorData.new(false,
_('Add Readme'),
statistic_icon + _('Add README'),
add_readme_path)
elsif repository.readme
AnchorData.new(true,
_('Readme'),
default_view != 'readme' ? readme_path : '#readme')
AnchorData.new(false,
statistic_icon('doc-text') + _('README'),
default_view != 'readme' ? readme_path : '#readme',
'default',
'doc-text')
end
end
 
def changelog_anchor_data
if current_user && can_current_user_push_to_default_branch? && repository.changelog.blank?
AnchorData.new(false,
_('Add Changelog'),
statistic_icon + _('Add CHANGELOG'),
add_changelog_path)
elsif repository.changelog.present?
AnchorData.new(true,
_('Changelog'),
changelog_path)
AnchorData.new(false,
statistic_icon('doc-text') + _('CHANGELOG'),
changelog_path,
'default')
end
end
 
def license_anchor_data
icon = statistic_icon('scale')
if repository.license_blob.present?
AnchorData.new(true,
license_short_name,
icon + content_tag(:strong, license_short_name, class: 'project-stat-value'),
license_path)
else
if current_user && can_current_user_push_to_default_branch?
AnchorData.new(false,
_('Add license'),
AnchorData.new(true,
content_tag(:span, icon + _('Add license'), class: 'add-license-link d-flex'),
add_license_path)
else
AnchorData.new(false,
_('No license. All rights reserved'),
AnchorData.new(true,
icon + content_tag(:strong, _('No license. All rights reserved'), class: 'project-stat-value'),
nil)
end
end
Loading
Loading
@@ -225,22 +251,29 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def contribution_guide_anchor_data
if current_user && can_current_user_push_to_default_branch? && repository.contribution_guide.blank?
AnchorData.new(false,
_('Add Contribution guide'),
statistic_icon + _('Add CONTRIBUTING'),
add_contribution_guide_path)
elsif repository.contribution_guide.present?
AnchorData.new(true,
_('Contribution guide'),
AnchorData.new(false,
statistic_icon('doc-text') + _('CONTRIBUTING'),
contribution_guide_path)
end
end
 
def autodevops_anchor_data(show_auto_devops_callout: false)
if current_user && can?(current_user, :admin_pipeline, project) && repository.gitlab_ci_yml.blank? && !show_auto_devops_callout
AnchorData.new(auto_devops_enabled?,
auto_devops_enabled? ? _('Auto DevOps enabled') : _('Enable Auto DevOps'),
project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
if auto_devops_enabled?
AnchorData.new(false,
statistic_icon('doc-text') + _('Auto DevOps enabled'),
project_settings_ci_cd_path(project, anchor: 'autodevops-settings'),
'default')
else
AnchorData.new(false,
statistic_icon + _('Enable Auto DevOps'),
project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end
elsif auto_devops_enabled?
AnchorData.new(true,
AnchorData.new(false,
_('Auto DevOps enabled'),
nil)
end
Loading
Loading
@@ -248,27 +281,32 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
 
def kubernetes_cluster_anchor_data
if current_user && can?(current_user, :create_cluster, project)
cluster_link = clusters.count == 1 ? project_cluster_path(project, clusters.first) : project_clusters_path(project)
 
if clusters.empty?
cluster_link = new_project_cluster_path(project)
end
AnchorData.new(false,
statistic_icon + _('Add Kubernetes cluster'),
new_project_cluster_path(project))
else
cluster_link = clusters.count == 1 ? project_cluster_path(project, clusters.first) : project_clusters_path(project)
 
AnchorData.new(!clusters.empty?,
clusters.empty? ? _('Add Kubernetes cluster') : _('Kubernetes configured'),
cluster_link)
AnchorData.new(false,
_('Kubernetes configured'),
cluster_link,
'default')
end
end
end
 
def gitlab_ci_anchor_data
if current_user && can_current_user_push_code? && repository.gitlab_ci_yml.blank? && !auto_devops_enabled?
AnchorData.new(false,
_('Set up CI/CD'),
statistic_icon + _('Set up CI/CD'),
add_ci_yml_path)
elsif repository.gitlab_ci_yml.present?
AnchorData.new(true,
_('CI/CD configuration'),
ci_configuration_path)
AnchorData.new(false,
statistic_icon('doc-text') + _('CI/CD configuration'),
ci_configuration_path,
'default')
end
end
 
Loading
Loading
.group-home-panel.text-center
.group-home-panel.text-center.border-bottom
%div{ class: container_class }
.avatar-container.s70.group-avatar
= group_icon(@group, class: "avatar s70 avatar-tile")
Loading
Loading
- is_project_overview = local_assigns.fetch(:is_project_overview, false)
- commit = local_assigns.fetch(:commit) { @repository.commit }
- ref = local_assigns.fetch(:ref) { current_ref }
- project = local_assigns.fetch(:project) { @project }
- content_url = local_assigns.fetch(:content_url) { @tree.readme ? project_blob_path(@project, tree_join(@ref, @tree.readme.path)) : project_tree_path(@project, @ref) }
- show_auto_devops_callout = show_auto_devops_callout?(@project)
 
#tree-holder.tree-holder.clearfix
.nav-block
Loading
Loading
@@ -10,4 +12,8 @@
- if commit
= render 'shared/commit_well', commit: commit, ref: ref, project: project
 
- if is_project_overview
.project-buttons.append-bottom-default
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
= render 'projects/tree/tree_content', tree: @tree, content_url: content_url
- empty_repo = @project.empty_repo?
- license = @project.license_anchor_data
- show_auto_devops_callout = show_auto_devops_callout?(@project)
.project-home-panel{ class: ("empty-project" if empty_repo) }
.limit-container-width{ class: container_class }
.project-header.d-flex.flex-row.flex-wrap.align-items-center.append-bottom-8
.project-title-row.d-flex.align-items-center
.avatar-container.project-avatar.float-none
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s24', width: 24, height: 24)
%h1.project-title.d-flex.align-items-baseline.qa-project-name
= @project.name
.project-metadata.d-flex.flex-row.flex-wrap.align-items-baseline
.project-visibility.d-inline-flex.align-items-baseline.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) }
= visibility_level_icon(@project.visibility_level, fw: false, options: {class: 'icon'})
= visibility_level_label(@project.visibility_level)
- if license.present?
.project-license.d-inline-flex.align-items-baseline
= link_to_if license.link, sprite_icon('scale', size: 16, css_class: 'icon') + license.label, license.link, class: license.enabled ? 'btn btn-link btn-secondary-hover-link' : 'btn btn-link'
- if @project.tag_list.present?
.project-tag-list.d-inline-flex.align-items-baseline.has-tooltip{ data: { container: 'body' }, title: @project.has_extra_tags? ? @project.tag_list.join(', ') : nil }
= sprite_icon('tag', size: 16, css_class: 'icon')
= @project.tags_to_show
- if @project.has_extra_tags?
= _("+ %{count} more") % { count: @project.count_of_extra_tags_not_shown }
.project-header.row.append-bottom-8
.project-title-row.col-md-12.col-lg-7.d-flex
.avatar-container.project-avatar.float-none
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s64', width: 64, height: 64)
.d-flex.flex-column.flex-wrap.align-items-baseline
.d-inline-flex.align-items-baseline
%h1.project-title.qa-project-name
= @project.name
%span.project-visibility.prepend-left-8.d-inline-flex.align-items-baseline.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) }
= visibility_level_icon(@project.visibility_level, fw: false, options: {class: 'icon'})
.project-metadata.d-flex.align-items-center
- if can?(current_user, :read_project, @project)
%span.text-secondary
= s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
- if current_user
%span.access-request-links.prepend-left-8
= render 'shared/members/access_request_links', source: @project
- if @project.tag_list.present?
%span.project-tag-list.d-inline-flex.prepend-left-8.has-tooltip{ data: { container: 'body' }, title: @project.has_extra_tags? ? @project.tag_list.join(', ') : nil }
= sprite_icon('tag', size: 16, css_class: 'icon append-right-4')
= @project.tags_to_show
- if @project.has_extra_tags?
= _("+ %{count} more") % { count: @project.count_of_extra_tags_not_shown }
 
.project-home-desc
- if @project.description.present?
.project-description
.project-description-markdown.read-more-container
= markdown_field(@project, :description)
%button.btn.btn-blank.btn-link.text-secondary.js-read-more-trigger.text-secondary.d-lg-none{ type: "button" }
= _("Read more")
- if can?(current_user, :read_project, @project)
.text-secondary.prepend-top-8
= s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
- if @project.forked?
%p
- if @project.fork_source
#{ s_('ForkedFromProjectPath|Forked from') }
= link_to project_path(@project.fork_source) do
= fork_source_name(@project)
- else
- deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)')
= deleted_message % { project_name: fork_source_name(@project) }
- if @project.badges.present?
.project-badges.prepend-top-default.append-bottom-default
- @project.badges.each do |badge|
%a.append-right-8{ href: badge.rendered_link_url(@project),
target: '_blank',
rel: 'noopener noreferrer' }>
%img.project-badge{ src: badge.rendered_image_url(@project),
'aria-hidden': true,
alt: 'Project badge' }>
.project-repo-buttons.col-md-12.col-lg-5.d-inline-flex.flex-wrap.justify-content-lg-end
- if current_user
.d-inline-flex
= render 'projects/buttons/notifications', notification_setting: @notification_setting, btn_class: 'btn-xs'
 
.project-repo-buttons.d-inline-flex.flex-wrap
.count-buttons.d-inline-flex
= render 'projects/buttons/star'
= render 'projects/buttons/fork'
 
- if can?(current_user, :download_code, @project)
.project-clone-holder.d-inline-flex.d-sm-none
.project-clone-holder.d-inline-flex.d-md-none.btn-block
= render "shared/mobile_clone_panel"
 
.project-clone-holder.d-none.d-sm-inline-flex
= render "shared/clone_panel"
.project-clone-holder.d-none.d-md-inline-flex
= render "projects/buttons/clone"
 
- if show_xcode_link?(@project)
.project-action-button.project-xcode.inline
= render "projects/buttons/xcode_link"
- if can?(current_user, :download_code, @project)
%nav.project-stats
.nav-links.quick-links.mt-3
= render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
 
- if current_user
- if can?(current_user, :download_code, @project)
.d-none.d-sm-inline-flex
= render 'projects/buttons/download', project: @project, ref: @ref
.d-none.d-sm-inline-flex
= render 'projects/buttons/dropdown'
.project-home-desc.mt-1
- if @project.description.present?
.project-description
.project-description-markdown.read-more-container
= markdown_field(@project, :description)
%button.btn.btn-blank.btn-link.js-read-more-trigger.d-lg-none{ type: "button" }
= _("Read more")
- if @project.forked?
%p
- if @project.fork_source
#{ s_('ForkedFromProjectPath|Forked from') }
= link_to project_path(@project.fork_source) do
= fork_source_name(@project)
- else
- deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)')
= deleted_message % { project_name: fork_source_name(@project) }
 
.d-none.d-sm-inline-flex
= render 'shared/notifications/button', notification_setting: @notification_setting
.d-none.d-sm-inline-flex
= render 'shared/members/access_request_buttons', source: @project
- if @project.badges.present?
.project-badges.mb-2
- @project.badges.each do |badge|
%a.append-right-8{ href: badge.rendered_link_url(@project),
target: '_blank',
rel: 'noopener noreferrer' }>
%img.project-badge{ src: badge.rendered_image_url(@project),
'aria-hidden': true,
alt: 'Project badge' }>
Loading
Loading
@@ -4,5 +4,5 @@
%ul.nav
- anchors.each do |anchor|
%li.nav-item
= link_to_if anchor.link, anchor.label, anchor.link, class: anchor.enabled ? 'nav-link stat-link' : "nav-link btn btn-#{anchor.class_modifier || 'missing'}" do
.stat-text= anchor.label
= link_to_if anchor.link, anchor.label, anchor.link, class: anchor.is_link ? 'nav-link stat-link d-flex align-items-center' : "nav-link btn btn-#{anchor.class_modifier || 'missing'} d-flex align-items-center" do
.stat-text.d-flex.align-items-center= anchor.label
- project = project || @project
.git-clone-holder.js-git-clone-holder.input-group
- if allowed_protocols_present?
.input-group-text.clone-dropdown-btn.btn
%span.js-clone-dropdown-label
= enabled_project_button(project, enabled_protocol)
- else
%a#clone-dropdown.input-group-text.btn.btn-primary.btn-xs.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
%span.append-right-4.js-clone-dropdown-label
= _('Clone')
= sprite_icon("arrow-down", css_class: "icon")
%form.p-3.dropdown-menu.dropdown-menu-right.dropdown-menu-large.dropdown-menu-selectable.clone-options-dropdown
%li.pb-2
%label.label-bold
= _('Clone with SSH')
.input-group
= text_field_tag :ssh_project_clone, project.ssh_url_to_repo, class: "js-select-on-focus form-control", readonly: true, aria: { label: 'Project clone URL' }
.input-group-append
= clipboard_button(target: '#ssh_project_clone', title: _("Copy URL to clipboard"), class: "input-group-text btn-default btn-clipboard")
= render_if_exists 'projects/buttons/geo'
%li
%label.label-bold
= _('Clone with %{http_label}') % { http_label: gitlab_config.protocol.upcase }
.input-group
= text_field_tag :http_project_clone, project.http_url_to_repo, class: "js-select-on-focus form-control", readonly: true, aria: { label: 'Project clone URL' }
.input-group-append
= clipboard_button(target: '#http_project_clone', title: _("Copy URL to clipboard"), class: "input-group-text btn-default btn-clipboard")
= render_if_exists 'projects/buttons/geo'
= render_if_exists 'shared/geo_info_modal', project: project
Loading
Loading
@@ -5,8 +5,8 @@
.project-action-button.dropdown.inline>
%button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download'), 'data-display' => 'static' }
= sprite_icon('download')
= icon("caret-down")
%span.sr-only= _('Select Archive Format')
= sprite_icon("arrow-down")
%ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
%li.dropdown-header
#{ _('Source code') }
Loading
Loading
- unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project)
.count-badge.d-inline-flex.align-item-stretch.append-right-8
%span.fork-count.count-badge-count.d-flex.align-items-center
= link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Fork'), s_('ProjectOverview|Forks'), @project.forks_count), class: 'count' do
= @project.forks_count
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'btn btn-default has-tooltip count-badge-button d-flex align-items-center fork-btn' do
= sprite_icon('fork', { css_class: 'icon' })
Loading
Loading
@@ -15,3 +12,6 @@
title: (s_('ProjectOverview|You have reached your project limit') unless can_create_fork) do
= sprite_icon('fork', { css_class: 'icon' })
%span= s_('ProjectOverview|Fork')
%span.fork-count.count-badge-count.d-flex.align-items-center
= link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Fork'), s_('ProjectOverview|Forks'), @project.forks_count), class: 'count' do
= @project.forks_count
- btn_class = local_assigns.fetch(:btn_class, "btn-xs")
- if notification_setting
.js-notification-dropdown.notification-dropdown.project-action-button.dropdown.inline
= form_for notification_setting, remote: true, html: { class: "inline notification-form no-label" } do |f|
= hidden_setting_source_input(notification_setting)
= hidden_field_tag "hide_label", true
= f.hidden_field :level, class: "notification_setting_level"
.js-notification-toggle-btns
%div{ class: ("btn-group" if notification_setting.custom?) }
- if notification_setting.custom?
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }, class: "#{btn_class}", "aria-label" => _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }, data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
= sprite_icon("notifications", css_class: "icon notifications-icon js-notifications-icon")
%span.js-notification-loading.fa.hidden
%button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= sprite_icon("arrow-down", css_class: "icon")
.sr-only Toggle dropdown
- else
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting - #{notification_title(notification_setting.level)}", class: "#{btn_class}", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= sprite_icon("notifications", css_class: "icon notifications-icon js-notifications-icon")
%span.js-notification-loading.fa.hidden
= sprite_icon("arrow-down", css_class: "icon")
= render "shared/notifications/notification_dropdown", notification_setting: notification_setting
= content_for :scripts_body do
= render "shared/notifications/custom_notifications", notification_setting: notification_setting
- if current_user
.count-badge.d-inline-flex.align-item-stretch.append-right-8
%span.star-count.count-badge-count.d-flex.align-items-center
= @project.star_count
%button.count-badge-button.btn.btn-default.d-flex.align-items-center.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
%button.count-badge-button.btn.btn-default.btn-xs.d-flex.align-items-center.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
- if current_user.starred?(@project)
= sprite_icon('star', { css_class: 'icon' })
%span.starred= s_('ProjectOverview|Unstar')
- else
= sprite_icon('star-o', { css_class: 'icon' })
%span= s_('ProjectOverview|Star')
%span.star-count.count-badge-count.d-flex.align-items-center
= @project.star_count
 
- else
.count-badge.d-inline-flex.align-item-stretch.append-right-8
%span.star-count.count-badge-count.d-flex.align-items-center
= @project.star_count
= link_to new_user_session_path, class: 'btn btn-default has-tooltip count-badge-button d-flex align-items-center star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
= link_to new_user_session_path, class: 'btn btn-default btn-xs has-tooltip count-badge-button d-flex align-items-center star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
= sprite_icon('star-o', { css_class: 'icon' })
%span= s_('ProjectOverview|Star')
%span.star-count.count-badge-count.d-flex.align-items-center
= @project.star_count
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment