Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
7b935cc8
Commit
7b935cc8
authored
Apr 13, 2017
by
Clement Ho
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[skip ci] refactor down into one component
parent
025fd9ce
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
232 additions
and
278 deletions
+232
-278
app/assets/javascripts/sidebar_assignees/components/assignees.js
...ets/javascripts/sidebar_assignees/components/assignees.js
+181
-0
app/assets/javascripts/sidebar_assignees/components/collapsed/assignees.js
...ripts/sidebar_assignees/components/collapsed/assignees.js
+0
-79
app/assets/javascripts/sidebar_assignees/components/collapsed/avatar.js
...ascripts/sidebar_assignees/components/collapsed/avatar.js
+0
-27
app/assets/javascripts/sidebar_assignees/components/expanded/multiple_assignees.js
...debar_assignees/components/expanded/multiple_assignees.js
+0
-72
app/assets/javascripts/sidebar_assignees/components/expanded/no_assignee.js
...ipts/sidebar_assignees/components/expanded/no_assignee.js
+0
-20
app/assets/javascripts/sidebar_assignees/components/expanded/single_assignee.js
.../sidebar_assignees/components/expanded/single_assignee.js
+0
-36
app/assets/javascripts/sidebar_assignees/services/sidebar_assignees_service.js
...s/sidebar_assignees/services/sidebar_assignees_service.js
+3
-2
app/assets/javascripts/sidebar_assignees/sidebar_assignees_options.js
...avascripts/sidebar_assignees/sidebar_assignees_options.js
+15
-26
app/assets/javascripts/sidebar_assignees/stores/sidebar_assignees_store.js
...ripts/sidebar_assignees/stores/sidebar_assignees_store.js
+14
-15
app/assets/javascripts/users_select.js
app/assets/javascripts/users_select.js
+18
-0
app/views/shared/issuable/_sidebar.html.haml
app/views/shared/issuable/_sidebar.html.haml
+1
-1
No files found.
app/assets/javascripts/sidebar_assignees/components/assignees.js
0 → 100644
View file @
7b935cc8
import
eventHub
from
'
../event_hub
'
;
export
default
{
name
:
'
Assignees
'
,
data
()
{
return
{
defaultRenderCount
:
5
,
defaultMaxCounter
:
99
,
showLess
:
true
,
};
},
props
:
{
rootPath
:
{
type
:
String
,
required
:
true
,
},
users
:
{
type
:
Array
,
required
:
true
,
},
},
computed
:
{
renderShowMoreSection
()
{
return
this
.
users
.
length
>
this
.
defaultRenderCount
;
},
numberOfHiddenAssignees
()
{
return
this
.
users
.
length
-
this
.
defaultRenderCount
;
},
isHiddenAssignees
()
{
return
this
.
numberOfHiddenAssignees
>
0
;
},
collapsedTooltipTitle
()
{
const
maxRender
=
Math
.
min
(
this
.
defaultRenderCount
,
this
.
users
.
length
);
const
renderUsers
=
this
.
users
.
slice
(
0
,
maxRender
);
const
names
=
renderUsers
.
map
(
u
=>
u
.
name
);
if
(
this
.
users
.
length
>
maxRender
)
{
names
.
push
(
`+
${
this
.
users
.
length
-
maxRender
}
more`
);
}
return
names
.
join
(
'
,
'
);
},
sidebarAvatarCounter
()
{
let
counter
=
`+
${
this
.
users
.
length
-
1
}
`
;
if
(
this
.
users
.
length
>
this
.
defaultMaxCounter
)
{
counter
=
`
${
this
.
defaultMaxCounter
}
+`
;
}
return
counter
;
},
},
methods
:
{
assignSelf
()
{
eventHub
.
$emit
(
'
addCurrentUser
'
);
},
toggleShowLess
()
{
this
.
showLess
=
!
this
.
showLess
;
},
renderAssignee
(
index
)
{
return
!
this
.
showLess
||
(
index
<
this
.
defaultRenderCount
&&
this
.
showLess
);
},
assigneeUrl
(
user
)
{
return
`
${
this
.
rootPath
}${
user
.
username
}
`
;
},
assigneeAlt
(
user
)
{
return
`
${
user
.
name
}
's avatar`
;
},
},
template
:
`
<div>
<div
class="sidebar-collapsed-icon sidebar-collapsed-user"
:class="{ 'multiple-users': users.length > 1, 'has-tooltip': users.length > 0}"
data-container="body"
data-placement="left"
:title="collapsedTooltipTitle"
>
<i
v-if="users.length === 0"
aria-hidden="true"
class="fa fa-user"
/>
<button
type="button"
class="btn-link"
v-for="(user, index) in users"
v-if="index === 0 || users.length <= 2 && index <= 2"
>
<img
width="24"
class="avatar avatar-inline s24"
:alt="assigneeAlt(user)"
:src="user.avatarUrl"
>
<span class="author">{{user.name}}</span>
</button>
<button
v-if="users.length > 2"
class="btn-link"
type="button"
>
<span
class="avatar-counter sidebar-avatar-counter"
>
{{sidebarAvatarCounter}}
</span>
</button>
</div>
<div class="value hide-collapsed">
<template v-if="users.length === 0">
<span class="assign-yourself no-value">
No assignee -
<button
type="button"
class="btn-link"
@click="assignSelf"
>
assign yourself
</button>
</span>
</template>
<template v-else-if="users.length === 1">
<a
class="author_link bold"
:href="assigneeUrl(users[0])"
>
<img
width="32"
class="avatar avatar-inline s32"
:alt="assigneeAlt(users[0])"
:src="users[0].avatarUrl"
>
<span class="author">{{users[0].name}}</span>
<span class="username">@{{users[0].username}}</span>
</a>
</template>
<template v-else>
<div class="user-list">
<div
class="user-item"
v-for="(user, index) in users"
v-if="renderAssignee(index)"
>
<a
class="user-link has-tooltip"
data-placement="bottom"
:href="assigneeUrl(user)"
:data-title="user.name"
>
<img
width="32"
class="avatar avatar-inline s32"
:alt="assigneeAlt(user)"
:src="user.avatarUrl"
/>
</a>
</div>
</div>
<div
v-if="renderShowMoreSection"
class="user-list-more"
>
<button
type="button"
class="btn-link"
@click="toggleShowLess"
>
<template v-if="showLess">
+ {{numberOfHiddenAssignees}} more
</template>
<template v-else>
- show less
</template>
</button>
</div>
</template>
</div>
</div>
`
,
};
app/assets/javascripts/sidebar_assignees/components/collapsed/assignees.js
deleted
100644 → 0
View file @
025fd9ce
import
CollapsedAvatar
from
'
./avatar
'
;
export
default
{
name
:
'
CollapsedAssignees
'
,
data
()
{
return
{
defaultRenderCount
:
5
,
defaultMaxCounter
:
99
,
};
},
props
:
{
users
:
{
type
:
Array
,
required
:
true
,
},
},
computed
:
{
title
()
{
const
maxRender
=
Math
.
min
(
this
.
defaultRenderCount
,
this
.
users
.
length
);
const
renderUsers
=
this
.
users
.
slice
(
0
,
maxRender
);
const
names
=
renderUsers
.
map
(
u
=>
u
.
name
);
if
(
this
.
users
.
length
>
maxRender
)
{
names
.
push
(
`+
${
this
.
users
.
length
-
maxRender
}
more`
);
}
return
names
.
join
(
'
,
'
);
},
counter
()
{
let
counter
=
`+
${
this
.
users
.
length
-
1
}
`
;
if
(
this
.
users
.
length
>
this
.
defaultMaxCounter
)
{
counter
=
`
${
this
.
defaultMaxCounter
}
+`
;
}
return
counter
;
},
hasNoAssignees
()
{
return
this
.
users
.
length
===
0
;
},
hasTwoAssignees
()
{
return
this
.
users
.
length
===
2
;
},
moreThanOneAssignees
()
{
return
this
.
users
.
length
>
1
;
},
moreThanTwoAssignees
()
{
return
this
.
users
.
length
>
2
;
},
},
components
:
{
'
collapsed-avatar
'
:
CollapsedAvatar
,
},
template
:
`
<div>
<div v-if="hasNoAssignees" class="sidebar-collapsed-icon sidebar-collapsed-user">
<i aria-hidden="true" class="fa fa-user"></i>
</div>
<div v-else class="sidebar-collapsed-icon sidebar-collapsed-user has-tooltip"
:class="{'multiple-users': moreThanOneAssignees}"
data-container="body"
data-placement="left"
:title="title" >
<collapsed-avatar
:name="users[0].name"
:avatarUrl="users[0].avatarUrl"
/>
<collapsed-avatar
v-if="hasTwoAssignees"
:name="users[1].name"
:avatarUrl="users[1].avatarUrl"
/>
<button class="btn-link" type="button" v-if="moreThanTwoAssignees">
<span class="avatar-counter sidebar-avatar-counter">{{counter}}</span>
</button>
</div>
</div>
`
,
};
app/assets/javascripts/sidebar_assignees/components/collapsed/avatar.js
deleted
100644 → 0
View file @
025fd9ce
export
default
{
name
:
'
CollapsedAvatar
'
,
props
:
{
name
:
{
type
:
String
,
required
:
true
,
},
avatarUrl
:
{
type
:
String
,
required
:
true
,
},
},
computed
:
{
alt
()
{
return
`
${
this
.
name
}
's avatar`
;
},
},
template
:
`
<button class="btn-link" type="button">
<img width="24"
class="avatar avatar-inline s24"
:alt="alt"
:src="avatarUrl" >
<span class="author">{{name}}</span>
</button>
`
,
};
app/assets/javascripts/sidebar_assignees/components/expanded/multiple_assignees.js
deleted
100644 → 0
View file @
025fd9ce
export
default
{
name
:
'
MultipleAssignees
'
,
data
()
{
return
{
defaultRenderCount
:
5
,
showLess
:
true
,
};
},
props
:
{
rootPath
:
{
type
:
String
,
required
:
true
,
},
users
:
{
type
:
Array
,
required
:
true
,
},
},
computed
:
{
renderShowMoreSection
()
{
return
this
.
users
.
length
>
this
.
defaultRenderCount
;
},
numberOfHiddenAssignees
()
{
return
this
.
users
.
length
-
this
.
defaultRenderCount
;
},
isHiddenAssignees
()
{
return
this
.
numberOfHiddenAssignees
>
0
;
},
},
methods
:
{
toggleShowLess
()
{
this
.
showLess
=
!
this
.
showLess
;
},
renderAssignee
(
index
)
{
return
!
this
.
showLess
||
(
index
<
this
.
defaultRenderCount
&&
this
.
showLess
);
},
assigneeUrl
(
username
)
{
return
`
${
this
.
rootPath
}${
username
}
`
;
},
assigneeAlt
(
name
)
{
return
`
${
name
}
's avatar`
;
},
},
template
:
`
<div class="hide-collapsed">
<div class="user-list">
<div class="user-item" v-for="(user, index) in users"
v-if="renderAssignee(index)" >
<a class="user-link has-tooltip"
data-placement="bottom"
:href="assigneeUrl(user.username)"
:data-title="user.name" >
<img width="32"
class="avatar avatar-inline s32"
:alt="assigneeAlt(user.name)"
:src="user.avatarUrl" >
</a>
</div>
</div>
<div class="user-list-more" v-if="renderShowMoreSection">
<button type="button" class="btn-link" @click="toggleShowLess">
<template v-if="showLess">
+ {{numberOfHiddenAssignees}} more
</template>
<template v-else>
- show less
</template>
</button>
</div>
</div>
`
,
};
app/assets/javascripts/sidebar_assignees/components/expanded/no_assignee.js
deleted
100644 → 0
View file @
025fd9ce
import
eventHub
from
'
../../event_hub
'
;
export
default
{
name
:
'
NoAssignee
'
,
methods
:
{
assignSelf
()
{
eventHub
.
$emit
(
'
addCurrentUser
'
);
},
},
template
:
`
<div class="hide-collapsed">
<span class="assign-yourself no-value">
No assignee -
<button type="button" class="btn-link" @click="assignSelf">
assign yourself
</button>
</span>
</div>
`
,
};
app/assets/javascripts/sidebar_assignees/components/expanded/single_assignee.js
deleted
100644 → 0
View file @
025fd9ce
export
default
{
name
:
'
SingleAssignee
'
,
props
:
{
rootPath
:
{
type
:
String
,
required
:
true
,
},
user
:
{
type
:
Object
,
required
:
true
,
},
},
computed
:
{
userUrl
()
{
return
`
${
this
.
rootPath
}${
this
.
user
.
username
}
`
;
},
username
()
{
return
`@
${
this
.
user
.
username
}
`
;
},
avatarAlt
()
{
return
`
${
this
.
user
.
name
}
's avatar`
;
},
},
template
:
`
<div class="hide-collapsed">
<a class="author_link bold" :href="userUrl">
<img width="32"
class="avatar avatar-inline s32"
:alt="avatarAlt"
:src="user.avatarUrl" >
<span class="author">{{user.name}}</span>
<span class="username">{{username}}</span>
</a>
</div>
`
,
};
app/assets/javascripts/sidebar_assignees/services/sidebar_assignees_service.js
View file @
7b935cc8
...
...
@@ -2,7 +2,6 @@ import Vue from 'vue';
import
VueResource
from
'
vue-resource
'
;
import
'
../../vue_shared/vue_resource_interceptor
'
;
Vue
.
http
.
options
.
emulateJSON
=
true
;
Vue
.
use
(
VueResource
);
export
default
class
SidebarAssigneesService
{
...
...
@@ -14,6 +13,8 @@ export default class SidebarAssigneesService {
update
(
userIds
)
{
return
Vue
.
http
.
put
(
this
.
path
,
{
[
this
.
field
]:
userIds
,
},
{
emulateJSON
:
true
});
},
{
emulateJSON
:
true
,
});
}
}
app/assets/javascripts/sidebar_assignees/sidebar_assignees_options.js
View file @
7b935cc8
...
...
@@ -3,11 +3,7 @@
import
eventHub
from
'
./event_hub
'
;
import
AssigneeTitle
from
'
./components/assignee_title
'
;
import
NoAssignee
from
'
./components/expanded/no_assignee
'
;
import
SingleAssignee
from
'
./components/expanded/single_assignee
'
;
import
MultipleAssignees
from
'
./components/expanded/multiple_assignees
'
;
import
CollapsedAssignees
from
'
./components/collapsed/assignees
'
;
import
Assignees
from
'
./components/assignees
'
;
import
SidebarAssigneesService
from
'
./services/sidebar_assignees_service
'
;
import
SidebarAssigneesStore
from
'
./stores/sidebar_assignees_store
'
;
...
...
@@ -55,6 +51,10 @@ export default {
methods
:
{
addCurrentUser
()
{
this
.
store
.
addCurrentUserId
();
// Notify gl dropdown that we are now assigning to current user
this
.
$el
.
parentElement
.
dispatchEvent
(
new
Event
(
'
assignYourself
'
));
this
.
saveUsers
();
},
saveUsers
()
{
...
...
@@ -63,41 +63,30 @@ export default {
.
then
((
response
)
=>
{
this
.
loading
=
false
;
this
.
store
.
setUsers
(
response
.
data
.
assignees
);
}).
catch
(()
=>
{
})
.
catch
(()
=>
{
this
.
loading
=
false
;
return
new
Flash
(
'
An error occured while saving assignees
'
);
});
},
},
components
:
{
'
no-assignee
'
:
NoAssignee
,
'
single-assignee
'
:
SingleAssignee
,
'
multiple-assignees
'
:
MultipleAssignees
,
'
assignee-title
'
:
AssigneeTitle
,
'
collapsed-assignees
'
:
Collapsed
Assignees
,
'
assignees
'
:
Assignees
,
},
template
:
`
<div>
<assignee-title
:numberOfAssignees="store.
u
serIds.length"
:numberOfAssignees="store.
selectedU
serIds.length"
:loading="loading"
:editable="store.editable"
/>
<collapsed-assignees :users="store.users"/>
<div class="value" v-if="!store.loading">
<no-assignee v-if="numberOfAssignees === 0" />
<single-assignee
v-else-if="numberOfAssignees === 1"
:rootPath="store.rootPath"
:user="store.users[0]"
/>
<multiple-assignees
v-else
:rootPath="store.rootPath"
:users="store.users"
/>
</div>
<assignees
class="value"
v-if="!store.loading"
:rootPath="store.rootPath"
:users="store.renderedUsers"
/>
</div>
`
,
};
app/assets/javascripts/sidebar_assignees/stores/sidebar_assignees_store.js
View file @
7b935cc8
...
...
@@ -5,47 +5,46 @@ export default class SidebarAssigneesStore {
this
.
currentUserId
=
currentUserId
;
this
.
rootPath
=
rootPath
;
// Tracks the selected users
this
.
userIds
=
[];
// Tracks the rendered users
this
.
users
=
[];
this
.
selectedUserIds
=
[];
this
.
renderedUsers
=
[];
this
.
loading
=
false
;
this
.
editable
=
editable
;
this
.
setUsers
(
assignees
);
}
this
.
userIds
=
assignees
.
map
(
a
=>
a
.
id
);
addCurrentUserId
()
{
this
.
addUserId
(
this
.
currentUserId
);
}
addUserId
(
id
)
{
this
.
userIds
.
push
(
id
);
// Prevent duplicate user id's from being added
if
(
this
.
selectedUserIds
.
indexOf
(
id
)
===
-
1
)
{
this
.
selectedUserIds
.
push
(
id
);
}
}
removeUserId
(
id
)
{
this
.
userIds
=
this
.
u
serIds
.
filter
(
uid
=>
uid
!==
id
);
this
.
selectedUserIds
=
this
.
selectedU
serIds
.
filter
(
uid
=>
uid
!==
id
);
}
removeAllUserIds
()
{
this
.
userIds
=
[];
}
addCurrentUserId
()
{
this
.
addUserId
(
this
.
currentUserId
);
this
.
selectedUserIds
=
[];
}
getUserIds
()
{
// If there are no ids, that means we have to unassign (which is id = 0)
return
this
.
userIds
.
length
>
0
?
this
.
u
serIds
:
[
0
];
return
this
.
selectedUserIds
.
length
>
0
?
this
.
selectedU
serIds
:
[
0
];
}
setUsers
(
users
)
{
this
.
u
sers
=
users
.
map
((
u
)
=>
({
this
.
renderedU
sers
=
users
.
map
((
u
)
=>
({
id
:
u
.
id
,
name
:
u
.
name
,
username
:
u
.
username
,
avatarUrl
:
u
.
avatar_url
,
}));
this
.
selectedUserIds
=
users
.
map
(
u
=>
u
.
id
);
}
}
app/assets/javascripts/users_select.js
View file @
7b935cc8
...
...
@@ -54,6 +54,24 @@ import eventHub from './sidebar_assignees/event_hub';
$collapsedSidebar
=
$block
.
find
(
'
.sidebar-collapsed-user
'
);
$loading
=
$block
.
find
(
'
.block-loading
'
).
fadeOut
();
$block
[
0
].
addEventListener
(
'
assignYourself
'
,
()
=>
{
// Remove unassigned selected from the DOM
const
unassignedSelected
=
$dropdown
.
closest
(
'
.selectbox
'
)
.
find
(
"
input[name='
"
+
(
$dropdown
.
data
(
'
field-name
'
))
+
"
'][value=0]
"
);
if
(
unassignedSelected
)
{
unassignedSelected
.
remove
();
}
// Save current selected user to the DOM
const
input
=
document
.
createElement
(
'
input
'
);
input
.
type
=
'
hidden
'
;
input
.
name
=
$dropdown
.
data
(
'
field-name
'
);
input
.
value
=
_this
.
currentUser
.
id
;
$dropdown
.
before
(
input
);
});
var
getSelected
=
function
()
{
return
$selectbox
.
find
(
`input[name="
${
$dropdown
.
data
(
'
field-name
'
)}
"]`
)
...
...
app/views/shared/issuable/_sidebar.html.haml
View file @
7b935cc8
...
...
@@ -66,7 +66,7 @@
.selectbox.hide-collapsed
-
issuable
.
assignees
.
each
do
|
assignee
|
=
hidden_field_tag
"
#{
issuable
.
to_ability_name
}
[assignee_ids][]"
,
assignee
.
id
,
id:
nil
,
data:
{
name:
assignee
.
name
,
username:
assignee
.
username
,
'avatar-url'
=>
assignee
.
avatar_url
}
=
hidden_field_tag
"
#{
issuable
.
to_ability_name
}
[assignee_ids][]"
,
assignee
.
id
,
id:
nil
-
options
=
{
toggle_class:
'js-user-search js-author-search'
,
title:
'Assign to'
,
filter:
true
,
dropdown_class:
'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author'
,
placeholder:
'Search users'
,
data:
{
first_user:
(
current_user
.
username
if
current_user
),
current_user:
true
,
project_id:
(
@project
.
id
if
@project
),
author_id:
issuable
.
author_id
,
field_name:
"
#{
issuable
.
to_ability_name
}
[assignee_id]"
,
issue_update:
issuable_json_path
(
issuable
),
ability_name:
issuable
.
to_ability_name
,
null_user:
true
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment