diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/pages/dashboard/projects/index.js | 6 | ||||
-rw-r--r-- | app/assets/javascripts/pages/root/index.js | 5 | ||||
-rw-r--r-- | app/assets/javascripts/pages/users/user_tabs.js | 6 | ||||
-rw-r--r-- | app/assets/javascripts/star.js | 15 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/avatar.scss | 2 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/variables.scss | 2 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/projects.scss | 202 | ||||
-rw-r--r-- | app/controllers/users_controller.rb | 4 | ||||
-rw-r--r-- | app/helpers/projects_helper.rb | 14 | ||||
-rw-r--r-- | app/views/shared/projects/_list.html.haml | 13 | ||||
-rw-r--r-- | app/views/shared/projects/_project.html.haml | 141 |
11 files changed, 337 insertions, 73 deletions
diff --git a/app/assets/javascripts/pages/dashboard/projects/index.js b/app/assets/javascripts/pages/dashboard/projects/index.js index 0c585e162cb..8f98be79640 100644 --- a/app/assets/javascripts/pages/dashboard/projects/index.js +++ b/app/assets/javascripts/pages/dashboard/projects/index.js @@ -1,3 +1,7 @@ import ProjectsList from '~/projects_list'; +import Star from '../../../star'; -document.addEventListener('DOMContentLoaded', () => new ProjectsList()); +document.addEventListener('DOMContentLoaded', () => { + new ProjectsList(); // eslint-disable-line no-new + new Star('.project-row'); // eslint-disable-line no-new +}); diff --git a/app/assets/javascripts/pages/root/index.js b/app/assets/javascripts/pages/root/index.js new file mode 100644 index 00000000000..09f8185d3b5 --- /dev/null +++ b/app/assets/javascripts/pages/root/index.js @@ -0,0 +1,5 @@ +// if the "projects dashboard" is a user's default dashboard, when they visit the +// instance root index, the dashboard will be served by the root controller instead +// of a dashboard controller. The root index redirects for all other default dashboards. + +import '../dashboard/projects/index'; diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index aa537d4a43e..006eaa08cd1 100644 --- a/app/assets/javascripts/pages/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -151,8 +151,10 @@ export default class UserTabs { loadTab(action, endpoint) { this.toggleLoading(true); + const params = action === 'projects' ? { skip_namespace: true } : {}; + return axios - .get(endpoint) + .get(endpoint, { params }) .then(({ data }) => { const tabSelector = `div#${action}`; this.$parentEl.find(tabSelector).html(data.html); @@ -188,7 +190,7 @@ export default class UserTabs { requestParams: { limit: 10 }, }); UserTabs.renderMostRecentBlocks('#js-overview .projects-block', { - requestParams: { limit: 10, skip_pagination: true }, + requestParams: { limit: 10, skip_pagination: true, skip_namespace: true, compact_mode: true }, }); this.loaded.overview = true; diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js index 9af5d5b23cb..7404dfbf22a 100644 --- a/app/assets/javascripts/star.js +++ b/app/assets/javascripts/star.js @@ -5,11 +5,12 @@ import { spriteIcon } from './lib/utils/common_utils'; import axios from './lib/utils/axios_utils'; export default class Star { - constructor() { - $('.project-home-panel .toggle-star').on('click', function toggleStarClickCallback() { + constructor(container = '.project-home-panel') { + $(`${container} .toggle-star`).on('click', function toggleStarClickCallback() { const $this = $(this); const $starSpan = $this.find('span'); - const $startIcon = $this.find('svg'); + const $starIcon = $this.find('svg'); + const iconClasses = $starIcon.attr('class').split(' '); axios .post($this.data('endpoint')) @@ -22,12 +23,12 @@ export default class Star { if (isStarred) { $starSpan.removeClass('starred').text(s__('StarProject|Star')); - $startIcon.remove(); - $this.prepend(spriteIcon('star-o', 'icon')); + $starIcon.remove(); + $this.prepend(spriteIcon('star-o', iconClasses)); } else { $starSpan.addClass('starred').text(__('Unstar')); - $startIcon.remove(); - $this.prepend(spriteIcon('star', 'icon')); + $starIcon.remove(); + $this.prepend(spriteIcon('star', iconClasses)); } }) .catch(() => Flash('Star toggle failed. Try again later.')); diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index 054c75912ea..e132aa4c216 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -108,6 +108,7 @@ width: 100%; height: 100%; display: flex; + text-decoration: none; } .avatar { @@ -120,6 +121,7 @@ } &.s40 { min-width: 40px; min-height: 40px; } + &.s64 { min-width: 64px; min-height: 64px; } } .avatar-counter { diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 5310195d9c5..0fc8cc10b7c 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -198,6 +198,7 @@ $well-light-text-color: #5b6169; $gl-font-size: 14px; $gl-font-size-xs: 11px; $gl-font-size-small: 12px; +$gl-font-size-medium: 1.43rem; $gl-font-size-large: 16px; $gl-font-weight-normal: 400; $gl-font-weight-bold: 600; @@ -276,6 +277,7 @@ $project-title-row-height: 64px; $project-avatar-mobile-size: 24px; $gl-line-height: 16px; $gl-line-height-24: 24px; +$gl-line-height-14: 14px; /* * Common component specific colors diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 278800aba95..0ce0db038a7 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -969,34 +969,73 @@ pre.light-well { @include basic-list-stats; display: flex; align-items: center; - } + color: $gl-text-color-secondary; + padding: $gl-padding 0; - h3 { - font-size: $gl-font-size; + @include media-breakpoint-up(lg) { + padding: $gl-padding-24 0; + } + + &.no-description { + @include media-breakpoint-up(sm) { + .avatar-container { + align-self: center; + } + + .metadata-info { + margin-bottom: 0; + } + } + } } - .avatar-container, - .controls { - flex: 0 0 auto; + h2 { + font-size: $gl-font-size-medium; + font-weight: $gl-font-weight-bold; + margin-bottom: 0; + + @include media-breakpoint-up(sm) { + .namespace-name { + font-weight: $gl-font-weight-normal; + } + } } .avatar-container { + flex: 0 0 auto; align-self: flex-start; } .project-details { min-width: 0; + line-height: $gl-line-height; + + .flex-wrapper { + min-width: 0; + margin-top: -$gl-padding-8; // negative margin required for flex-wrap + } p, .commit-row-message { @include str-truncated(100%); margin-bottom: 0; } - } - .controls { - margin-left: auto; - text-align: right; + .user-access-role { + margin: 0; + } + + @include media-breakpoint-up(md) { + .description { + color: $gl-text-color; + } + } + + @include media-breakpoint-down(md) { + .user-access-role { + line-height: $gl-line-height-14; + } + } } .ci-status-link { @@ -1008,6 +1047,149 @@ pre.light-well { text-decoration: none; } } + + .controls { + margin-top: $gl-padding; + + @include media-breakpoint-down(md) { + margin-top: 0; + } + + @include media-breakpoint-down(xs) { + margin-top: $gl-padding-8; + } + + .icon-wrapper { + color: inherit; + margin-right: $gl-padding; + + @include media-breakpoint-down(md) { + margin-right: 0; + margin-left: $gl-padding-8; + } + + @include media-breakpoint-down(xs) { + &:first-child { + margin-left: 0; + } + } + } + + .ci-status-link { + display: inline-flex; + } + } + + .star-button { + .icon { + top: 0; + } + } + + .icon-container { + @include media-breakpoint-down(xs) { + margin-right: $gl-padding-8; + } + } + + &.compact { + .project-row { + padding: $gl-padding 0; + } + + h2 { + font-size: $gl-font-size; + } + + .avatar-container { + @include avatar-size(40px, 10px); + min-height: 40px; + min-width: 40px; + + .identicon.s64 { + font-size: 16px; + } + } + + .controls { + @include media-breakpoint-up(sm) { + margin-top: 0; + } + } + + .updated-note { + @include media-breakpoint-up(sm) { + margin-top: $gl-padding-8; + } + } + + .icon-wrapper { + margin-left: $gl-padding-8; + margin-right: 0; + + @include media-breakpoint-down(xs) { + &:first-child { + margin-left: 0; + } + } + } + + .user-access-role { + line-height: $gl-line-height-14; + } + } + + @include media-breakpoint-down(md) { + h2 { + font-size: $gl-font-size; + } + + .avatar-container { + @include avatar-size(40px, 10px); + min-height: 40px; + min-width: 40px; + + .identicon.s64 { + font-size: 16px; + } + } + } + + @include media-breakpoint-down(md) { + .updated-note { + margin-top: $gl-padding-8; + text-align: right; + } + } + + .forks, + .pipeline-status, + .updated-note { + display: flex; + } + + @include media-breakpoint-down(md) { + &:not(.explore) { + .forks { + display: none; + + } + } + + &.explore { + .pipeline-status, + .updated-note { + display: none !important; + } + } + } + + @include media-breakpoint-down(xs) { + .updated-note { + margin-top: 0; + text-align: left; + } + } } .card .projects-list li { diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 8b040dc080e..072d62ddf38 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -58,11 +58,13 @@ class UsersController < ApplicationController load_projects skip_pagination = Gitlab::Utils.to_boolean(params[:skip_pagination]) + skip_namespace = Gitlab::Utils.to_boolean(params[:skip_namespace]) + compact_mode = Gitlab::Utils.to_boolean(params[:compact_mode]) respond_to do |format| format.html { render 'show' } format.json do - pager_json("shared/projects/_list", @projects.count, projects: @projects, skip_pagination: skip_pagination) + pager_json("shared/projects/_list", @projects.count, projects: @projects, skip_pagination: skip_pagination, skip_namespace: skip_namespace, compact_mode: compact_mode) end end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 7c8557a1a8a..1186eb3ddcc 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -515,6 +515,20 @@ module ProjectsHelper end end + def explore_projects_tab? + current_page?(explore_projects_path) || + current_page?(trending_explore_projects_path) || + current_page?(starred_explore_projects_path) + end + + def show_merge_request_count?(merge_requests, compact_mode) + merge_requests && !compact_mode && Feature.enabled?(:project_list_show_mr_count, default_enabled: true) + end + + def show_issue_count?(issues, compact_mode) + issues && !compact_mode && Feature.enabled?(:project_list_show_issue_count, default_enabled: true) + end + def sidebar_projects_paths %w[ projects#show diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml index 06eb3d03e31..15c29e14cc0 100644 --- a/app/views/shared/projects/_list.html.haml +++ b/app/views/shared/projects/_list.html.haml @@ -2,24 +2,29 @@ - avatar = true unless local_assigns[:avatar] == false - use_creator_avatar = false unless local_assigns[:use_creator_avatar] == true - stars = true unless local_assigns[:stars] == false -- forks = false unless local_assigns[:forks] == true +- forks = true unless local_assigns[:forks] == false +- merge_requests = true unless local_assigns[:merge_requests] == false +- issues = true unless local_assigns[:issues] == false +- pipeline_status = true unless local_assigns[:pipeline_status] == false - ci = false unless local_assigns[:ci] == true - skip_namespace = false unless local_assigns[:skip_namespace] == true - user = local_assigns[:user] - show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true - remote = false unless local_assigns[:remote] == true - skip_pagination = false unless local_assigns[:skip_pagination] == true +- compact_mode = false unless local_assigns[:compact_mode] == true +- css_classes = "#{'compact' if compact_mode} #{'explore' if explore_projects_tab?}" .js-projects-list-holder - if any_projects?(projects) - load_pipeline_status(projects) - - %ul.projects-list + %ul.projects-list{ class: css_classes } - projects.each_with_index do |project, i| - css_class = (i >= projects_limit) || project.pending_delete? ? 'hide' : nil = render "shared/projects/project", project: project, skip_namespace: skip_namespace, avatar: avatar, stars: stars, css_class: css_class, ci: ci, use_creator_avatar: use_creator_avatar, - forks: forks, show_last_commit_as_description: show_last_commit_as_description, user: user + forks: forks, show_last_commit_as_description: show_last_commit_as_description, user: user, merge_requests: merge_requests, + issues: issues, pipeline_status: pipeline_status, compact_mode: compact_mode - if @private_forks_count && @private_forks_count > 0 %li.project-row.private-forks-notice diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index aba790e1217..9dde77fccef 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -1,62 +1,107 @@ - avatar = true unless local_assigns[:avatar] == false - stars = true unless local_assigns[:stars] == false -- forks = false unless local_assigns[:forks] == true +- forks = true unless local_assigns[:forks] == false +- merge_requests = true unless local_assigns[:merge_requests] == false +- issues = true unless local_assigns[:issues] == false +- pipeline_status = true unless local_assigns[:pipeline_status] == false - skip_namespace = false unless local_assigns[:skip_namespace] == true - access = max_project_member_access(project) -- css_class = '' unless local_assigns[:css_class] +- compact_mode = false unless local_assigns[:compact_mode] == true - show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && can_show_last_commit_in_list?(project) +- css_class = '' unless local_assigns[:css_class] - css_class += " no-description" if project.description.blank? && !show_last_commit_as_description - cache_key = project_list_cache_key(project) - updated_tooltip = time_ago_with_tooltip(project.last_activity_date) +- css_details_class = compact_mode ? "d-flex flex-column flex-sm-row flex-md-row align-items-sm-center" : "align-items-center flex-md-fill flex-lg-column d-sm-flex d-lg-block" +- css_controls_class = compact_mode ? "" : "align-items-md-end align-items-lg-center flex-lg-row" -%li.project-row{ class: css_class } +%li.project-row.d-flex{ class: css_class } = cache(cache_key) do - if avatar - .avatar-container.s40 + .avatar-container.s64.flex-grow-0.flex-shrink-0 = link_to project_path(project), class: dom_class(project) do - if project.creator && use_creator_avatar - = image_tag avatar_icon_for_user(project.creator, 40), class: "avatar s40", alt:'' + = image_tag avatar_icon_for_user(project.creator, 64), class: "avatar s65", alt:'' - else - = project_icon(project, alt: '', class: 'avatar project-avatar s40', width: 40, height: 40) - .project-details - %h3.prepend-top-0.append-bottom-0 - = link_to project_path(project), class: 'text-plain' do - %span.project-full-name>< - %span.namespace-name - - if project.namespace && !skip_namespace - = project.namespace.human_name - \/ - %span.project-name< - = project.name - - - if access&.nonzero? - -# haml-lint:disable UnnecessaryStringOutput - = ' ' # prevent haml from eating the space between elements - %span.user-access-role= Gitlab::Access.human_access(access) - - - if show_last_commit_as_description - .description.prepend-top-5 - = link_to_markdown(project.commit.title, project_commit_path(project, project.commit), class: "commit-row-message") - - elsif project.description.present? - .description.prepend-top-5 - = markdown_field(project, :description) - - .controls - .prepend-top-0 - - if project.archived - %span.prepend-left-10.badge.badge-warning archived - - if can?(current_user, :read_cross_project) && project.pipeline_status.has_status? - %span.prepend-left-10 - = render_project_pipeline_status(project.pipeline_status) - - if forks - %span.prepend-left-10 - = sprite_icon('fork', size: 12) - = number_with_delimiter(project.forks_count) - - if stars - %span.prepend-left-10 - = icon('star') - = number_with_delimiter(project.star_count) - %span.prepend-left-10.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project) } - = visibility_level_icon(project.visibility_level, fw: true) - .prepend-top-0 - updated #{updated_tooltip} + = project_icon(project, alt: '', class: 'avatar project-avatar s64', width: 64, height: 64) + .project-details.flex-sm-fill{ class: css_details_class } + .flex-wrapper.flex-fill + .d-flex.align-items-center.flex-wrap + %h2.d-flex.prepend-top-8 + = link_to project_path(project), class: 'text-plain' do + %span.project-full-name.append-right-8>< + %span.namespace-name + - if project.namespace && !skip_namespace + = project.namespace.human_name + \/ + %span.project-name< + = project.name + + %span.metadata-info.visibility-icon.append-right-10.prepend-top-8.has-tooltip{ data: { container: 'body', placement: 'top' }, title: visibility_icon_description(project) } + = visibility_level_icon(project.visibility_level, fw: true) + + - if explore_projects_tab? && project.repository.license + %span.metadata-info.d-inline-flex.align-items-center.append-right-10.prepend-top-8 + = sprite_icon('scale', size: 14, css_class: 'append-right-4') + = project.repository.license.name + + - if !explore_projects_tab? && access&.nonzero? + -# haml-lint:disable UnnecessaryStringOutput + = ' ' # prevent haml from eating the space between elements + .metadata-info.prepend-top-8 + %span.user-access-role.d-block= Gitlab::Access.human_access(access) + + - if show_last_commit_as_description + .description.d-none.d-sm-block.prepend-top-8.append-right-default + = link_to_markdown(project.commit.title, project_commit_path(project, project.commit), class: "commit-row-message") + - elsif project.description.present? + .description.d-none.d-sm-block.prepend-top-8.append-right-default + = markdown_field(project, :description) + + .controls.d-flex.flex-row.flex-sm-column.flex-md-column.align-items-center.align-items-sm-end.flex-wrap.flex-shrink-0{ class: css_controls_class } + .icon-container.d-flex.align-items-center + - if project.archived + %span.d-flex.icon-wrapper.badge.badge-warning archived + - if stars + %span.d-flex.align-items-center.icon-wrapper.stars.has-tooltip{ data: { container: 'body', placement: 'top' }, title: _('Stars') } + = sprite_icon('star', size: 14, css_class: 'append-right-4') + = number_with_delimiter(project.star_count) + - if forks + = link_to project_forks_path(project), + class: "align-items-center icon-wrapper forks has-tooltip", + title: _('Forks'), data: { container: 'body', placement: 'top' } do + = sprite_icon('fork', size: 14, css_class: 'append-right-4') + = number_with_delimiter(project.forks_count) + - if show_merge_request_count?(merge_requests, compact_mode) + = link_to project_merge_requests_path(project), + class: "d-none d-lg-flex align-items-center icon-wrapper merge-requests has-tooltip", + title: _('Merge Requests'), data: { container: 'body', placement: 'top' } do + = sprite_icon('git-merge', size: 14, css_class: 'append-right-4') + = number_with_delimiter(project.open_merge_requests_count) + - if show_issue_count?(issues, compact_mode) + = link_to project_issues_path(project), + class: "d-none d-lg-flex align-items-center icon-wrapper issues has-tooltip", + title: _('Issues'), data: { container: 'body', placement: 'top' } do + = sprite_icon('issues', size: 14, css_class: 'append-right-4') + = number_with_delimiter(project.open_issues_count) + - if pipeline_status && can?(current_user, :read_cross_project) && project.pipeline_status.has_status? + %span.icon-wrapper.pipeline-status + = render_project_pipeline_status(project.pipeline_status, tooltip_placement: 'top') + .updated-note + %span Updated #{updated_tooltip} + + .d-none.d-lg-flex.align-item-stretch + - unless compact_mode + - if current_user + %button.star-button.btn.btn-default.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') + + - else + = 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 + = sprite_icon('star-o', { css_class: 'icon' }) + %span= s_('ProjectOverview|Star') |