Skip to content
Snippets Groups Projects
Commit 388d4170 authored by Martin Wortschack's avatar Martin Wortschack Committed by Sean McGivern
Browse files

Resolve "Redesign project lists UI"

parent b96150d8
No related branches found
No related tags found
No related merge requests found
Showing
with 348 additions and 73 deletions
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
});
// 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';
Loading
Loading
@@ -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);
Loading
Loading
@@ -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;
Loading
Loading
Loading
Loading
@@ -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'))
Loading
Loading
@@ -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.'));
Loading
Loading
Loading
Loading
@@ -108,6 +108,7 @@
width: 100%;
height: 100%;
display: flex;
text-decoration: none;
}
 
.avatar {
Loading
Loading
@@ -120,6 +121,7 @@
}
 
&.s40 { min-width: 40px; min-height: 40px; }
&.s64 { min-width: 64px; min-height: 64px; }
}
 
.avatar-counter {
Loading
Loading
Loading
Loading
@@ -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;
Loading
Loading
@@ -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
Loading
Loading
Loading
Loading
@@ -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 {
Loading
Loading
@@ -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 {
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
- 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')
---
title: Redesign project lists UI
merge_request: 22682
author:
type: other
Loading
Loading
@@ -3094,6 +3094,9 @@ msgstr ""
msgid "Forking in progress"
msgstr ""
 
msgid "Forks"
msgstr ""
msgid "Format"
msgstr ""
 
Loading
Loading
@@ -6266,6 +6269,9 @@ msgstr ""
msgid "Starred projects"
msgstr ""
 
msgid "Stars"
msgstr ""
msgid "Start a %{new_merge_request} with these changes"
msgstr ""
 
Loading
Loading
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