mirror of
https://github.com/overleaf/overleaf.git
synced 2024-09-16 02:52:31 -04:00
Merge pull request #491 from sharelatex/pr-project-list-improvements
Project list improvements
This commit is contained in:
commit
9d2d217d35
6 changed files with 124 additions and 12 deletions
|
@ -140,23 +140,35 @@
|
||||||
ng-repeat="project in visibleProjects | orderBy:predicate:reverse",
|
ng-repeat="project in visibleProjects | orderBy:predicate:reverse",
|
||||||
ng-controller="ProjectListItemController"
|
ng-controller="ProjectListItemController"
|
||||||
)
|
)
|
||||||
.row
|
.row(select-row)
|
||||||
.col-xs-6
|
.col-xs-6
|
||||||
input.select-item(
|
input.select-item(
|
||||||
select-individual,
|
select-individual,
|
||||||
type="checkbox",
|
type="checkbox",
|
||||||
ng-model="project.selected"
|
ng-model="project.selected"
|
||||||
|
stop-propagation="click"
|
||||||
)
|
)
|
||||||
span
|
span
|
||||||
a.projectName(href="/project/{{project.id}}") {{project.name}}
|
a.projectName(
|
||||||
|
href="/project/{{project.id}}"
|
||||||
|
stop-propagation="click"
|
||||||
|
) {{project.name}}
|
||||||
span(
|
span(
|
||||||
ng-controller="TagListController"
|
ng-controller="TagListController"
|
||||||
)
|
)
|
||||||
a.label.label-default.tag-label(
|
.tag-label(
|
||||||
|
ng-repeat='tag in project.tags'
|
||||||
|
stop-propagation="click"
|
||||||
|
)
|
||||||
|
a.label.label-default.tag-label-name(
|
||||||
href,
|
href,
|
||||||
ng-repeat='tag in project.tags',
|
|
||||||
ng-click="selectTag(tag)"
|
ng-click="selectTag(tag)"
|
||||||
) {{tag.name}}
|
) {{tag.name}}
|
||||||
|
a.label.label-default.tag-label-remove(
|
||||||
|
href
|
||||||
|
ng-click="removeProjectFromTag(project, tag)"
|
||||||
|
) ×
|
||||||
|
|
||||||
.col-xs-2
|
.col-xs-2
|
||||||
span.owner {{ownerName()}}
|
span.owner {{ownerName()}}
|
||||||
.col-xs-4
|
.col-xs-4
|
||||||
|
|
|
@ -79,6 +79,15 @@
|
||||||
li
|
li
|
||||||
a(href, ng-click="deleteTag(tag)", stop-propagation="click")
|
a(href, ng-click="deleteTag(tag)", stop-propagation="click")
|
||||||
| #{translate("delete")}
|
| #{translate("delete")}
|
||||||
|
li.tag.untagged(
|
||||||
|
ng-if="tags.length",
|
||||||
|
ng-cloak,
|
||||||
|
ng-click="selectUntagged()"
|
||||||
|
ng-class="{active: filter === 'untagged'}",
|
||||||
|
)
|
||||||
|
a.tag-name(href)
|
||||||
|
| #{translate("uncategorized")}
|
||||||
|
span.subdued ({{ nUntagged }})
|
||||||
li(ng-cloak)
|
li(ng-cloak)
|
||||||
a.tag(href, ng-click="openNewTagModal()")
|
a.tag(href, ng-click="openNewTagModal()")
|
||||||
i.fa.fa-fw.fa-plus
|
i.fa.fa-fw.fa-plus
|
||||||
|
|
|
@ -58,4 +58,18 @@ define [
|
||||||
scope.$apply () ->
|
scope.$apply () ->
|
||||||
scope.ngModel = false
|
scope.ngModel = false
|
||||||
ignoreChanges = false
|
ignoreChanges = false
|
||||||
|
|
||||||
|
scope.$on "select-all:row-clicked", () ->
|
||||||
|
ignoreChanges = true
|
||||||
|
scope.$apply () ->
|
||||||
|
scope.ngModel = !scope.ngModel
|
||||||
|
ignoreChanges = false
|
||||||
|
}
|
||||||
|
|
||||||
|
App.directive "selectRow", () ->
|
||||||
|
return {
|
||||||
|
scope: true
|
||||||
|
link: (scope, element, attrs) ->
|
||||||
|
element.on "click", (e) ->
|
||||||
|
scope.$broadcast "select-all:row-clicked"
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ define [
|
||||||
"base"
|
"base"
|
||||||
], (App) ->
|
], (App) ->
|
||||||
|
|
||||||
App.controller "ProjectPageController", ($scope, $modal, $q, $window, queuedHttp, event_tracking, $timeout) ->
|
App.controller "ProjectPageController", ($scope, $modal, $q, $window, queuedHttp, event_tracking, $timeout, localStorage) ->
|
||||||
$scope.projects = window.data.projects
|
$scope.projects = window.data.projects
|
||||||
$scope.tags = window.data.tags
|
$scope.tags = window.data.tags
|
||||||
$scope.notifications = window.data.notifications
|
$scope.notifications = window.data.notifications
|
||||||
|
@ -10,6 +10,7 @@ define [
|
||||||
$scope.selectedProjects = []
|
$scope.selectedProjects = []
|
||||||
$scope.filter = "all"
|
$scope.filter = "all"
|
||||||
$scope.predicate = "lastUpdated"
|
$scope.predicate = "lastUpdated"
|
||||||
|
$scope.nUntagged = 0
|
||||||
$scope.reverse = true
|
$scope.reverse = true
|
||||||
$scope.searchText =
|
$scope.searchText =
|
||||||
value : ""
|
value : ""
|
||||||
|
@ -18,6 +19,12 @@ define [
|
||||||
recalculateProjectListHeight()
|
recalculateProjectListHeight()
|
||||||
, 10
|
, 10
|
||||||
|
|
||||||
|
$scope.$watch((
|
||||||
|
() -> $scope.projects.filter((project) -> !project.tags? or project.tags.length == 0).length
|
||||||
|
), (newVal) -> $scope.nUntagged = newVal)
|
||||||
|
|
||||||
|
storedUIOpts = JSON.parse(localStorage("project_list"))
|
||||||
|
|
||||||
recalculateProjectListHeight = () ->
|
recalculateProjectListHeight = () ->
|
||||||
topOffset = $(".project-list-card")?.offset()?.top
|
topOffset = $(".project-list-card")?.offset()?.top
|
||||||
bottomOffset = $("footer").outerHeight() + 25
|
bottomOffset = $("footer").outerHeight() + 25
|
||||||
|
@ -52,6 +59,13 @@ define [
|
||||||
project.tags ||= []
|
project.tags ||= []
|
||||||
project.tags.push tag
|
project.tags.push tag
|
||||||
|
|
||||||
|
markTagAsSelected = (id) ->
|
||||||
|
for tag in $scope.tags
|
||||||
|
if tag._id == id
|
||||||
|
tag.selected = true
|
||||||
|
else
|
||||||
|
tag.selected = false
|
||||||
|
|
||||||
$scope.changePredicate = (newPredicate)->
|
$scope.changePredicate = (newPredicate)->
|
||||||
if $scope.predicate == newPredicate
|
if $scope.predicate == newPredicate
|
||||||
$scope.reverse = !$scope.reverse
|
$scope.reverse = !$scope.reverse
|
||||||
|
@ -104,6 +118,10 @@ define [
|
||||||
if $scope.filter == "tag" and selectedTag? and project.id not in selectedTag.project_ids
|
if $scope.filter == "tag" and selectedTag? and project.id not in selectedTag.project_ids
|
||||||
visible = false
|
visible = false
|
||||||
|
|
||||||
|
# Hide tagged projects if we only want to see the uncategorized ones
|
||||||
|
if $scope.filter == "untagged" and project.tags?.length > 0
|
||||||
|
visible = false
|
||||||
|
|
||||||
# Hide projects we own if we only want to see shared projects
|
# Hide projects we own if we only want to see shared projects
|
||||||
if $scope.filter == "shared" and project.accessLevel == "owner"
|
if $scope.filter == "shared" and project.accessLevel == "owner"
|
||||||
visible = false
|
visible = false
|
||||||
|
@ -126,6 +144,11 @@ define [
|
||||||
else
|
else
|
||||||
# We don't want hidden selections
|
# We don't want hidden selections
|
||||||
project.selected = false
|
project.selected = false
|
||||||
|
|
||||||
|
localStorage("project_list", JSON.stringify({
|
||||||
|
filter: $scope.filter,
|
||||||
|
selectedTagId: selectedTag?._id
|
||||||
|
}))
|
||||||
$scope.updateSelectedProjects()
|
$scope.updateSelectedProjects()
|
||||||
|
|
||||||
$scope.getSelectedTag = () ->
|
$scope.getSelectedTag = () ->
|
||||||
|
@ -178,6 +201,23 @@ define [
|
||||||
# the projects from view
|
# the projects from view
|
||||||
$scope.updateVisibleProjects()
|
$scope.updateVisibleProjects()
|
||||||
|
|
||||||
|
$scope.removeProjectFromTag = (project, tag) ->
|
||||||
|
tag.showWhenEmpty = true
|
||||||
|
|
||||||
|
project.tags ||= []
|
||||||
|
index = project.tags.indexOf tag
|
||||||
|
|
||||||
|
if index > -1
|
||||||
|
$scope._removeProjectIdsFromTagArray(tag, [ project.id ])
|
||||||
|
project.tags.splice(index, 1)
|
||||||
|
queuedHttp({
|
||||||
|
method: "DELETE"
|
||||||
|
url: "/tag/#{tag._id}/project/#{project.id}"
|
||||||
|
headers:
|
||||||
|
"X-CSRF-Token": window.csrfToken
|
||||||
|
})
|
||||||
|
$scope.updateVisibleProjects()
|
||||||
|
|
||||||
$scope.addSelectedProjectsToTag = (tag) ->
|
$scope.addSelectedProjectsToTag = (tag) ->
|
||||||
selected_projects = $scope.getSelectedProjects()
|
selected_projects = $scope.getSelectedProjects()
|
||||||
event_tracking.send 'project-list-page-interaction', 'project action', 'addSelectedProjectsToTag'
|
event_tracking.send 'project-list-page-interaction', 'project action', 'addSelectedProjectsToTag'
|
||||||
|
@ -425,6 +465,11 @@ define [
|
||||||
|
|
||||||
window.location = path
|
window.location = path
|
||||||
|
|
||||||
|
if storedUIOpts?.filter?
|
||||||
|
if storedUIOpts.filter == "tag" and storedUIOpts.selectedTagId?
|
||||||
|
markTagAsSelected(storedUIOpts.selectedTagId)
|
||||||
|
$scope.setFilter(storedUIOpts.filter)
|
||||||
|
else
|
||||||
$scope.updateVisibleProjects()
|
$scope.updateVisibleProjects()
|
||||||
|
|
||||||
App.controller "ProjectListItemController", ($scope) ->
|
App.controller "ProjectListItemController", ($scope) ->
|
||||||
|
|
|
@ -16,6 +16,10 @@ define [
|
||||||
tag.selected = true
|
tag.selected = true
|
||||||
$scope.setFilter("tag")
|
$scope.setFilter("tag")
|
||||||
|
|
||||||
|
$scope.selectUntagged = () ->
|
||||||
|
$scope._clearTags()
|
||||||
|
$scope.setFilter("untagged")
|
||||||
|
|
||||||
$scope.deleteTag = (tag) ->
|
$scope.deleteTag = (tag) ->
|
||||||
modalInstance = $modal.open(
|
modalInstance = $modal.open(
|
||||||
templateUrl: "deleteTagModalTemplate"
|
templateUrl: "deleteTagModalTemplate"
|
||||||
|
|
|
@ -124,6 +124,20 @@ ul.folders-menu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.untagged {
|
||||||
|
font-style: italic;
|
||||||
|
margin-bottom: @line-height-computed / 4;
|
||||||
|
a {
|
||||||
|
line-height: 1.7;
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span.subdued {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
&:not(.active) {
|
&:not(.active) {
|
||||||
background-color: darken(@gray-lightest, 2%);
|
background-color: darken(@gray-lightest, 2%);
|
||||||
|
@ -246,9 +260,23 @@ ul.project-list {
|
||||||
margin-left: @line-height-computed / 4;
|
margin-left: @line-height-computed / 4;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -2px;
|
top: -2px;
|
||||||
padding-top: 0.25em;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: white;
|
}
|
||||||
|
.tag-label-name,
|
||||||
|
.tag-label-remove {
|
||||||
|
display: inline-block;
|
||||||
|
padding-top: 0.3em;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
.tag-label-name {
|
||||||
|
padding-right: 0.3em;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
.tag-label-remove {
|
||||||
|
padding-left: 0.3em;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i.tablesort {
|
i.tablesort {
|
||||||
|
|
Loading…
Reference in a new issue