diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 892b3fab1c6..26c67fb721c 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -149,7 +149,6 @@ import './star'; import './subscription'; import './subscription_select'; import './syntax_highlight'; -import './user'; // eslint-disable-next-line global-require, import/no-commonjs if (process.env.NODE_ENV !== 'production') require('./test_utils/'); diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js new file mode 100644 index 00000000000..b7f50cfd083 --- /dev/null +++ b/app/assets/javascripts/users/activity_calendar.js @@ -0,0 +1,227 @@ +/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ + +import d3 from 'd3'; + +export default class ActivityCalendar { + constructor(timestamps, calendar_activities_path) { + this.calendar_activities_path = calendar_activities_path; + this.clickDay = this.clickDay.bind(this); + this.currentSelectedDate = ''; + this.daySpace = 1; + this.daySize = 15; + this.daySizeWithSpace = this.daySize + (this.daySpace * 2); + this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + this.months = []; + // Loop through the timestamps to create a group of objects + // The group of objects will be grouped based on the day of the week they are + this.timestampsTmp = []; + var group = 0; + + var today = new Date(); + today.setHours(0, 0, 0, 0, 0); + + var oneYearAgo = new Date(today); + oneYearAgo.setFullYear(today.getFullYear() - 1); + + var days = gl.utils.getDayDifference(oneYearAgo, today); + + for (var i = 0; i <= days; i += 1) { + var date = new Date(oneYearAgo); + date.setDate(date.getDate() + i); + + var day = date.getDay(); + var count = timestamps[date.format('yyyy-mm-dd')]; + + // Create a new group array if this is the first day of the week + // or if is first object + if ((day === 0 && i !== 0) || i === 0) { + this.timestampsTmp.push([]); + group += 1; + } + + var innerArray = this.timestampsTmp[group - 1]; + // Push to the inner array the values that will be used to render map + innerArray.push({ + count: count || 0, + date: date, + day: day + }); + } + + // Init color functions + this.colorKey = this.initColorKey(); + this.color = this.initColor(); + // Init the svg element + this.renderSvg(group); + this.renderDays(); + this.renderMonths(); + this.renderDayTitles(); + this.renderKey(); + this.initTooltips(); + } + + // Add extra padding for the last month label if it is also the last column + getExtraWidthPadding(group) { + var extraWidthPadding = 0; + var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth(); + var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth(); + + if (lastColMonth != secondLastColMonth) { + extraWidthPadding = 3; + } + + return extraWidthPadding; + } + + renderSvg(group) { + var width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group); + return this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar'); + } + + renderDays() { + return this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g').attr('transform', (function(_this) { + return function(group, i) { + _.each(group, function(stamp, a) { + var lastMonth, lastMonthX, month, x; + if (a === 0 && stamp.day === 0) { + month = stamp.date.getMonth(); + x = (_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace; + lastMonth = _.last(_this.months); + if (lastMonth != null) { + lastMonthX = lastMonth.x; + } + if (lastMonth == null) { + return _this.months.push({ + month: month, + x: x + }); + } else if (month !== lastMonth.month && x - _this.daySizeWithSpace !== lastMonthX) { + return _this.months.push({ + month: month, + x: x + }); + } + } + }); + return "translate(" + ((_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace) + ", 18)"; + }; + })(this)).selectAll('rect').data(function(stamp) { + return stamp; + }).enter().append('rect').attr('x', '0').attr('y', (function(_this) { + return function(stamp, i) { + return _this.daySizeWithSpace * stamp.day; + }; + })(this)).attr('width', this.daySize).attr('height', this.daySize).attr('title', (function(_this) { + return function(stamp) { + var contribText, date, dateText; + date = new Date(stamp.date); + contribText = 'No contributions'; + if (stamp.count > 0) { + contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : ''); + } + dateText = date.format('mmm d, yyyy'); + return contribText + "
" + (gl.utils.getDayName(date)) + " " + dateText; + }; + })(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) { + return function(stamp) { + if (stamp.count !== 0) { + return _this.color(Math.min(stamp.count, 40)); + } else { + return '#ededed'; + } + }; + })(this)).attr('data-container', 'body').on('click', this.clickDay); + } + + renderDayTitles() { + var days; + days = [ + { + text: 'M', + y: 29 + (this.daySizeWithSpace * 1) + }, { + text: 'W', + y: 29 + (this.daySizeWithSpace * 3) + }, { + text: 'F', + y: 29 + (this.daySizeWithSpace * 5) + } + ]; + return this.svg.append('g').selectAll('text').data(days).enter().append('text').attr('text-anchor', 'middle').attr('x', 8).attr('y', function(day) { + return day.y; + }).text(function(day) { + return day.text; + }).attr('class', 'user-contrib-text'); + } + + renderMonths() { + return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) { + return date.x; + }).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) { + return function(date) { + return _this.monthNames[date.month]; + }; + })(this)); + } + + renderKey() { + const keyValues = ['no contributions', '1-9 contributions', '10-19 contributions', '20-29 contributions', '30+ contributions']; + const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; + + this.svg.append('g') + .attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`) + .selectAll('rect') + .data(keyColors) + .enter() + .append('rect') + .attr('width', this.daySize) + .attr('height', this.daySize) + .attr('x', (color, i) => this.daySizeWithSpace * i) + .attr('y', 0) + .attr('fill', color => color) + .attr('class', 'js-tooltip') + .attr('title', (color, i) => keyValues[i]) + .attr('data-container', 'body'); + } + + initColor() { + var colorRange; + colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; + return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange); + } + + initColorKey() { + return d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]); + } + + clickDay(stamp) { + var formatted_date; + if (this.currentSelectedDate !== stamp.date) { + this.currentSelectedDate = stamp.date; + formatted_date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate(); + return $.ajax({ + url: this.calendar_activities_path, + data: { + date: formatted_date + }, + cache: false, + dataType: 'html', + beforeSend: function() { + return $('.user-calendar-activities').html('
'); + }, + success: function(data) { + return $('.user-calendar-activities').html(data); + } + }); + } else { + this.currentSelectedDate = ''; + return $('.user-calendar-activities').html(''); + } + } + + initTooltips() { + return $('.js-contrib-calendar .js-tooltip').tooltip({ + html: true + }); + } +} diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js deleted file mode 100644 index b11f691e424..00000000000 --- a/app/assets/javascripts/users/calendar.js +++ /dev/null @@ -1,231 +0,0 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len */ - -import d3 from 'd3'; - -(function() { - this.Calendar = (function() { - function Calendar(timestamps, calendar_activities_path) { - this.calendar_activities_path = calendar_activities_path; - this.clickDay = this.clickDay.bind(this); - this.currentSelectedDate = ''; - this.daySpace = 1; - this.daySize = 15; - this.daySizeWithSpace = this.daySize + (this.daySpace * 2); - this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; - this.months = []; - // Loop through the timestamps to create a group of objects - // The group of objects will be grouped based on the day of the week they are - this.timestampsTmp = []; - var group = 0; - - var today = new Date(); - today.setHours(0, 0, 0, 0, 0); - - var oneYearAgo = new Date(today); - oneYearAgo.setFullYear(today.getFullYear() - 1); - - var days = gl.utils.getDayDifference(oneYearAgo, today); - - for (var i = 0; i <= days; i += 1) { - var date = new Date(oneYearAgo); - date.setDate(date.getDate() + i); - - var day = date.getDay(); - var count = timestamps[date.format('yyyy-mm-dd')]; - - // Create a new group array if this is the first day of the week - // or if is first object - if ((day === 0 && i !== 0) || i === 0) { - this.timestampsTmp.push([]); - group += 1; - } - - var innerArray = this.timestampsTmp[group - 1]; - // Push to the inner array the values that will be used to render map - innerArray.push({ - count: count || 0, - date: date, - day: day - }); - } - - // Init color functions - this.colorKey = this.initColorKey(); - this.color = this.initColor(); - // Init the svg element - this.renderSvg(group); - this.renderDays(); - this.renderMonths(); - this.renderDayTitles(); - this.renderKey(); - this.initTooltips(); - } - - // Add extra padding for the last month label if it is also the last column - Calendar.prototype.getExtraWidthPadding = function(group) { - var extraWidthPadding = 0; - var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth(); - var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth(); - - if (lastColMonth != secondLastColMonth) { - extraWidthPadding = 3; - } - - return extraWidthPadding; - }; - - Calendar.prototype.renderSvg = function(group) { - var width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group); - return this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar'); - }; - - Calendar.prototype.renderDays = function() { - return this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g').attr('transform', (function(_this) { - return function(group, i) { - _.each(group, function(stamp, a) { - var lastMonth, lastMonthX, month, x; - if (a === 0 && stamp.day === 0) { - month = stamp.date.getMonth(); - x = (_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace; - lastMonth = _.last(_this.months); - if (lastMonth != null) { - lastMonthX = lastMonth.x; - } - if (lastMonth == null) { - return _this.months.push({ - month: month, - x: x - }); - } else if (month !== lastMonth.month && x - _this.daySizeWithSpace !== lastMonthX) { - return _this.months.push({ - month: month, - x: x - }); - } - } - }); - return "translate(" + ((_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace) + ", 18)"; - }; - })(this)).selectAll('rect').data(function(stamp) { - return stamp; - }).enter().append('rect').attr('x', '0').attr('y', (function(_this) { - return function(stamp, i) { - return _this.daySizeWithSpace * stamp.day; - }; - })(this)).attr('width', this.daySize).attr('height', this.daySize).attr('title', (function(_this) { - return function(stamp) { - var contribText, date, dateText; - date = new Date(stamp.date); - contribText = 'No contributions'; - if (stamp.count > 0) { - contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : ''); - } - dateText = date.format('mmm d, yyyy'); - return contribText + "
" + (gl.utils.getDayName(date)) + " " + dateText; - }; - })(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) { - return function(stamp) { - if (stamp.count !== 0) { - return _this.color(Math.min(stamp.count, 40)); - } else { - return '#ededed'; - } - }; - })(this)).attr('data-container', 'body').on('click', this.clickDay); - }; - - Calendar.prototype.renderDayTitles = function() { - var days; - days = [ - { - text: 'M', - y: 29 + (this.daySizeWithSpace * 1) - }, { - text: 'W', - y: 29 + (this.daySizeWithSpace * 3) - }, { - text: 'F', - y: 29 + (this.daySizeWithSpace * 5) - } - ]; - return this.svg.append('g').selectAll('text').data(days).enter().append('text').attr('text-anchor', 'middle').attr('x', 8).attr('y', function(day) { - return day.y; - }).text(function(day) { - return day.text; - }).attr('class', 'user-contrib-text'); - }; - - Calendar.prototype.renderMonths = function() { - return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) { - return date.x; - }).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) { - return function(date) { - return _this.monthNames[date.month]; - }; - })(this)); - }; - - Calendar.prototype.renderKey = function() { - const keyValues = ['no contributions', '1-9 contributions', '10-19 contributions', '20-29 contributions', '30+ contributions']; - const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; - - this.svg.append('g') - .attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`) - .selectAll('rect') - .data(keyColors) - .enter() - .append('rect') - .attr('width', this.daySize) - .attr('height', this.daySize) - .attr('x', (color, i) => this.daySizeWithSpace * i) - .attr('y', 0) - .attr('fill', color => color) - .attr('class', 'js-tooltip') - .attr('title', (color, i) => keyValues[i]) - .attr('data-container', 'body'); - }; - - Calendar.prototype.initColor = function() { - var colorRange; - colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; - return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange); - }; - - Calendar.prototype.initColorKey = function() { - return d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]); - }; - - Calendar.prototype.clickDay = function(stamp) { - var formatted_date; - if (this.currentSelectedDate !== stamp.date) { - this.currentSelectedDate = stamp.date; - formatted_date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate(); - return $.ajax({ - url: this.calendar_activities_path, - data: { - date: formatted_date - }, - cache: false, - dataType: 'html', - beforeSend: function() { - return $('.user-calendar-activities').html('
'); - }, - success: function(data) { - return $('.user-calendar-activities').html(data); - } - }); - } else { - this.currentSelectedDate = ''; - return $('.user-calendar-activities').html(''); - } - }; - - Calendar.prototype.initTooltips = function() { - return $('.js-contrib-calendar .js-tooltip').tooltip({ - html: true - }); - }; - - return Calendar; - })(); -}).call(window); diff --git a/app/assets/javascripts/users/index.js b/app/assets/javascripts/users/index.js new file mode 100644 index 00000000000..ecd8e09161e --- /dev/null +++ b/app/assets/javascripts/users/index.js @@ -0,0 +1,7 @@ +import ActivityCalendar from './activity_calendar'; +import User from './user'; + +// use legacy exports until embedded javascript is refactored +window.Calendar = ActivityCalendar; +window.gl = window.gl || {}; +window.gl.User = User; diff --git a/app/assets/javascripts/user.js b/app/assets/javascripts/users/user.js similarity index 69% rename from app/assets/javascripts/user.js rename to app/assets/javascripts/users/user.js index 9ef94ac7616..0b0a3e1afb4 100644 --- a/app/assets/javascripts/user.js +++ b/app/assets/javascripts/users/user.js @@ -1,9 +1,9 @@ -/* eslint-disable class-methods-use-this, comma-dangle, arrow-parens, no-param-reassign */ +/* eslint-disable class-methods-use-this */ import Cookies from 'js-cookie'; import UserTabs from './user_tabs'; -class User { +export default class User { constructor({ action }) { this.action = action; this.placeProfileAvatarsToTop(); @@ -13,25 +13,22 @@ class User { placeProfileAvatarsToTop() { $('.profile-groups-avatars').tooltip({ - placement: 'top' + placement: 'top', }); } initTabs() { return new UserTabs({ parentEl: '.user-profile', - action: this.action + action: this.action, }); } hideProjectLimitMessage() { - $('.hide-project-limit-message').on('click', e => { + $('.hide-project-limit-message').on('click', (e) => { e.preventDefault(); Cookies.set('hide_project_limit_message', 'false'); $(this).parents('.project-limit-message').remove(); }); } } - -window.gl = window.gl || {}; -window.gl.User = User; diff --git a/app/assets/javascripts/user_tabs.js b/app/assets/javascripts/users/user_tabs.js similarity index 100% rename from app/assets/javascripts/user_tabs.js rename to app/assets/javascripts/users/user_tabs.js diff --git a/app/assets/javascripts/users/users_bundle.js b/app/assets/javascripts/users/users_bundle.js deleted file mode 100644 index a38ce4eb25e..00000000000 --- a/app/assets/javascripts/users/users_bundle.js +++ /dev/null @@ -1 +0,0 @@ -import './calendar'; diff --git a/config/webpack.config.js b/config/webpack.config.js index c3fdca59a86..1113241e402 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -66,7 +66,7 @@ var config = { stl_viewer: './blob/stl_viewer.js', terminal: './terminal/terminal_bundle.js', u2f: ['vendor/u2f'], - users: './users/users_bundle.js', + users: './users/index.js', raven: './raven/index.js', vue_merge_request_widget: './vue_merge_request_widget/index.js', test: './test.js',