Commit ec4ad656 authored by Dennis Tang's avatar Dennis Tang Committed by Phil Hughes

Resolve "Improve project overview UI"

parent d32cec18
......@@ -13,40 +13,52 @@ export default class Project {
constructor() {
const $cloneOptions = $('ul.clone-options-dropdown');
const $projectCloneField = $('#project_clone');
const $cloneBtnText = $('a.clone-dropdown-btn span');
const $cloneBtnLabel = $('.js-git-clone-holder .js-clone-dropdown-label');
const selectedCloneOption = $cloneBtnText.text().trim();
const selectedCloneOption = $cloneBtnLabel.text().trim();
if (selectedCloneOption.length > 0) {
$(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
}
$('a', $cloneOptions).on('click', (e) => {
$('a', $cloneOptions).on('click', e => {
e.preventDefault();
const $this = $(e.currentTarget);
const url = $this.attr('href');
const activeText = $this.find('.dropdown-menu-inner-title').text();
const cloneType = $this.data('cloneType');
e.preventDefault();
$('.is-active', $cloneOptions).removeClass('is-active');
$(`a[data-clone-type="${cloneType}"]`).each(function() {
const $el = $(this);
const activeText = $el.find('.dropdown-menu-inner-title').text();
const $container = $el.closest('.project-clone-holder');
const $label = $container.find('.js-clone-dropdown-label');
$('.is-active', $cloneOptions).not($this).removeClass('is-active');
$this.toggleClass('is-active');
$projectCloneField.val(url);
$cloneBtnText.text(activeText);
$el.toggleClass('is-active');
$label.text(activeText);
});
return $('.clone').text(url);
$projectCloneField.val(url);
$('.js-git-empty .js-clone').text(url);
});
// Ref switcher
Project.initRefSwitcher();
$('.project-refs-select').on('change', function() {
return $(this).parents('form').submit();
return $(this)
.parents('form')
.submit();
});
$('.hide-no-ssh-message').on('click', function(e) {
Cookies.set('hide_no_ssh_message', 'false');
$(this).parents('.no-ssh-key-message').remove();
$(this)
.parents('.no-ssh-key-message')
.remove();
return e.preventDefault();
});
$('.hide-no-password-message').on('click', function(e) {
Cookies.set('hide_no_password_message', 'false');
$(this).parents('.no-password-message').remove();
$(this)
.parents('.no-password-message')
.remove();
return e.preventDefault();
});
Project.projectSelectDropdown();
......@@ -58,7 +70,7 @@ export default class Project {
}
static changeProject(url) {
return window.location = url;
return (window.location = url);
}
static initRefSwitcher() {
......@@ -73,14 +85,15 @@ export default class Project {
selected = $dropdown.data('selected');
return $dropdown.glDropdown({
data(term, callback) {
axios.get($dropdown.data('refsUrl'), {
params: {
ref: $dropdown.data('ref'),
search: term,
},
})
.then(({ data }) => callback(data))
.catch(() => flash(__('An error occurred while getting projects')));
axios
.get($dropdown.data('refsUrl'), {
params: {
ref: $dropdown.data('ref'),
search: term,
},
})
.then(({ data }) => callback(data))
.catch(() => flash(__('An error occurred while getting projects')));
},
selectable: true,
filterable: true,
......
......@@ -8,15 +8,18 @@ import BlobViewer from '~/blob/viewer/index';
import Activities from '~/activities';
import { ajaxGet } from '~/lib/utils/common_utils';
import GpgBadges from '~/gpg_badges';
import initReadMore from '~/read_more';
import Star from '../../../star';
import notificationsDropdown from '../../../notifications_dropdown';
document.addEventListener('DOMContentLoaded', () => {
initReadMore();
new Star(); // eslint-disable-line no-new
notificationsDropdown();
new ShortcutsNavigation(); // eslint-disable-line no-new
new NotificationsForm(); // eslint-disable-line no-new
new UserCallout({ // eslint-disable-line no-new
// eslint-disable-next-line no-new
new UserCallout({
setCalloutPerProject: false,
className: 'js-autodevops-banner',
});
......
/**
* ReadMore
*
* Adds "read more" functionality to elements.
*
* Specifically, it looks for a trigger, by default ".js-read-more-trigger", and adds the class
* "is-expanded" to the previous element in order to provide a click to expand functionality.
*
* This is useful for long text elements that you would like to truncate, especially for mobile.
*
* Example Markup
* <div class="read-more-container">
* <p>Some text that should be long enough to have to truncate within a specified container.</p>
* <p>This text will not appear in the container, as only the first line can be truncated.</p>
* <p>This should also not appear, if everything is working correctly!</p>
* </div>
* <button class="js-read-more-trigger">Read more</button>
*
*/
export default function initReadMore(triggerSelector = '.js-read-more-trigger') {
const triggerEls = document.querySelectorAll(triggerSelector);
if (!triggerEls) return;
triggerEls.forEach(triggerEl => {
const targetEl = triggerEl.previousElementSibling;
if (!targetEl) {
return;
}
triggerEl.addEventListener(
'click',
e => {
targetEl.classList.add('is-expanded');
e.target.remove();
},
{ once: true },
);
});
}
......@@ -64,3 +64,4 @@
@import 'framework/ci_variable_list';
@import 'framework/feature_highlight';
@import 'framework/terms';
@import 'framework/read_more';
......@@ -44,12 +44,8 @@
.project-repo-buttons {
display: block;
.count-buttons .btn {
margin: 0 10px;
}
.count-buttons .count-with-arrow {
display: none;
.count-buttons .count-badge {
margin-top: $gl-padding-8;
}
}
}
......
.read-more-container {
@include media-breakpoint-down(md) {
&:not(.is-expanded) {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
> * {
display: inline;
}
}
}
}
......@@ -271,6 +271,7 @@ $performance-bar-height: 35px;
$flash-height: 52px;
$context-header-height: 60px;
$breadcrumb-min-height: 48px;
$project-title-row-height: 24px;
/*
* Common component specific colors
......
......@@ -115,7 +115,7 @@
.project-feature-controls {
display: flex;
align-items: center;
margin: 8px 0;
margin: $gl-padding-8 0;
max-width: 432px;
.toggle-wrapper {
......@@ -144,12 +144,8 @@
.group-home-panel {
padding-top: 24px;
padding-bottom: 24px;
border-bottom: 1px solid $border-color;
@include media-breakpoint-up(sm) {
border-bottom: 1px solid $border-color;
}
.project-avatar,
.group-avatar {
float: none;
margin: 0 auto;
......@@ -175,7 +171,6 @@
}
}
.project-home-desc,
.group-home-desc {
margin-left: auto;
margin-right: auto;
......@@ -199,6 +194,62 @@
}
}
.project-home-panel {
padding-top: $gl-padding-8;
padding-bottom: $gl-padding-24;
.project-title-row {
margin-right: $gl-padding-8;
}
.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;
}
.project-title {
font-size: 20px;
line-height: $project-title-row-height;
font-weight: bold;
}
.project-metadata {
font-weight: normal;
font-size: 14px;
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 {
line-height: 0;
border-width: 0;
}
}
.project-tag-list,
.project-license {
.icon {
position: relative;
top: 2px;
}
}
}
}
.nav > .project-repo-buttons {
margin-top: 0;
}
......@@ -206,8 +257,6 @@
.project-repo-buttons,
.group-buttons {
.btn {
padding: 3px 10px;
&:last-child {
margin-left: 0;
}
......@@ -222,11 +271,15 @@
.fa-caret-down {
margin-left: 3px;
&.dropdown-btn-icon {
margin-left: 0;
}
}
}
.project-action-button {
margin: 15px 5px 0;
margin: $gl-padding $gl-padding-8 0 0;
vertical-align: top;
}
......@@ -243,82 +296,45 @@
.count-buttons {
display: inline-block;
vertical-align: top;
margin-top: 15px;
}
margin-top: $gl-padding;
.project-clone-holder {
display: inline-block;
margin: 15px 5px 0 0;
.count-badge {
height: $input-height;
input {
height: 28px;
.icon {
top: -1px;
}
}
}
.count-with-arrow {
display: inline-block;
position: relative;
margin-left: 4px;
.count-badge-count,
.count-badge-button {
border: 1px solid $border-color;
line-height: 1;
}
.arrow {
&::before {
content: '';
display: inline-block;
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
top: 50%;
left: 0;
margin-top: -6px;
border-width: 7px 5px 7px 0;
border-right-color: $count-arrow-border;
pointer-events: none;
}
.count,
.count-badge-button {
color: $gl-text-color;
}
&::after {
content: '';
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
top: 50%;
left: 1px;
margin-top: -9px;
border-width: 10px 7px 10px 0;
border-right-color: $white-light;
pointer-events: none;
}
.count-badge-count {
padding: 0 12px;
border-right: 0;
border-radius: $border-radius-base 0 0 $border-radius-base;
background: $gray-light;
}
.count {
@include btn-white;
display: inline-block;
background: $white-light;
border-radius: 2px;
border-width: 1px;
border-style: solid;
font-size: 13px;
font-weight: $gl-font-weight-bold;
line-height: 13px;
letter-spacing: 0.4px;
padding: 6px 14px;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
background-image: none;
white-space: nowrap;
margin: 0 10px 0 4px;
.count-badge-button {
border-radius: 0 $border-radius-base $border-radius-base 0;
}
}
a {
color: inherit;
}
.project-clone-holder {
display: inline-block;
margin: $gl-padding $gl-padding-8 0 0;
&:hover {
background: $white-light;
}
input {
height: $input-height;
}
}
......@@ -333,6 +349,14 @@
min-width: 320px;
}
}
.mobile-git-clone {
margin-top: $gl-padding-8;
.dropdown-menu-inner-content {
@extend .monospace;
}
}
}
.split-one {
......@@ -511,7 +535,6 @@
.controls {
margin-left: auto;
}
}
.choose-template {
......@@ -574,7 +597,7 @@
flex-wrap: wrap;
.btn {
padding: 8px;
padding: $gl-padding-8;
margin-right: 10px;
}
......@@ -651,7 +674,7 @@
left: -10px;
top: 50%;
z-index: 10;
padding: 8px 0;
padding: $gl-padding-8 0;
text-align: center;
background-color: $white-light;
color: $gl-text-color-tertiary;
......@@ -665,7 +688,7 @@
left: 50%;
top: 0;
transform: translateX(-50%);
padding: 0 8px;
padding: 0 $gl-padding-8;
}
}
......@@ -699,17 +722,51 @@
.project-stats {
font-size: 0;
text-align: center;
max-width: 100%;
border-bottom: 1px solid $border-color;
.nav {
margin-top: $gl-padding-8;
margin-bottom: $gl-padding-8;
.scrolling-tabs-container {
.scrolling-tabs {
margin-top: $gl-padding-8;
margin-bottom: $gl-padding-8;
flex-wrap: wrap;
border-bottom: 0;
}
.fade-left,
.fade-right {
top: 0;
height: 100%;
.fa {
top: 50%;
margin-top: -$gl-padding-8;
}
}
.nav {
flex-basis: 100%;
+ .nav {
margin: $gl-padding-8 0;
}
}
@include media-breakpoint-down(md) {
flex-direction: column;
.nav {
flex-wrap: nowrap;
}
.nav:first-child {
margin-right: $gl-padding-8;
}
}
}
.nav {
> li {
display: inline-block;
margin-top: $gl-padding-4;
margin-bottom: $gl-padding-4;
&:not(:last-child) {
margin-right: $gl-padding;
......@@ -732,13 +789,17 @@
font-size: $gl-font-size;
line-height: $gl-btn-line-height;
color: $gl-text-color-secondary;
white-space: nowrap;
}
.stat-link {
border-bottom: 0;
&:hover,
&:focus {
color: $gl-text-color;
text-decoration: underline;
border-bottom: 0;
}
}
......@@ -868,7 +929,7 @@ pre.light-well {
}
.git-clone-holder {
width: 380px;
width: 320px;
.btn-clipboard {
border: 1px solid $border-color;
......
......@@ -61,7 +61,7 @@ def http_clone_button(project, append_link: true)
dropdown_description = http_dropdown_description(protocol)
append_url = project.http_url_to_repo if append_link
dropdown_item_with_description(protocol, dropdown_description, href: append_url)
dropdown_item_with_description(protocol, dropdown_description, href: append_url, data: { clone_type: 'http' })
end
def http_dropdown_description(protocol)
......@@ -80,16 +80,17 @@ def ssh_clone_button(project, append_link: true)
append_url = project.ssh_url_to_repo if append_link
dropdown_item_with_description('SSH', dropdown_description, href: append_url)
dropdown_item_with_description('SSH', dropdown_description, href: append_url, data: { clone_type: 'ssh' })
end
def dropdown_item_with_description(title, description, href: nil)
def dropdown_item_with_description(title, description, href: nil, data: nil)
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",
href: (href if href)
href: (href if href),
data: (data if data)
end
end
......@@ -86,7 +86,7 @@ def boolean_to_icon(value)
end
end
def visibility_level_icon(level, fw: true)
def visibility_level_icon(level, fw: true, options: {})
name =
case level
when Gitlab::VisibilityLevel::PRIVATE
......@@ -99,7 +99,7 @@ def visibility_level_icon(level, fw: true)
name << " fw" if fw
icon(name)
icon(name, options)
end
def file_type_icon_class(type, mode, name)
......
......@@ -351,6 +351,10 @@ def default_url_to_repo(project = @project)
end
end
def default_clone_label
_("Copy %{protocol} clone URL") % { protocol: default_clone_protocol.upcase }
end
def default_clone_protocol
if allowed_protocols_present?
enabled_protocol
......
......@@ -138,7 +138,7 @@ def group_visibility_icon_description(level)
end