Skip to content

Commit b4e082f

Browse files
committed
UX: Refactor toolbar inputs and buttons with a three-dot menu photoprism#4830
Signed-off-by: Michael Mayer <michael@photoprism.app>
1 parent e9e0d3f commit b4e082f

File tree

14 files changed

+271
-232
lines changed

14 files changed

+271
-232
lines changed

frontend/src/component/album/toolbar.vue

Lines changed: 56 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -23,62 +23,65 @@
2323
{{ album.Title }}
2424
</v-toolbar-title>
2525

26-
<v-btn icon class="hidden-xs action-reload" :title="$gettext('Reload')" @click.stop="refresh()">
27-
<v-icon>mdi-refresh</v-icon>
28-
</v-btn>
29-
30-
<v-btn v-if="canManage" icon class="action-edit" :title="$gettext('Edit')" @click.stop="dialog.edit = true">
31-
<v-icon>mdi-pencil</v-icon>
32-
</v-btn>
33-
34-
<v-btn v-if="canShare" icon class="action-share" :title="$gettext('Share')" @click.stop="dialog.share = true">
35-
<v-icon>mdi-share-variant</v-icon>
36-
</v-btn>
37-
38-
<v-btn v-if="canDownload" icon class="action-download" :title="$gettext('Download')" @click.stop="download()">
39-
<v-icon>mdi-download</v-icon>
40-
</v-btn>
41-
42-
<v-btn
43-
v-if="settings.view === 'list'"
44-
icon
45-
class="action-view-mosaic"
46-
:title="$gettext('Toggle View')"
47-
@click.stop="setView('mosaic')"
48-
>
49-
<v-icon>mdi-view-comfy</v-icon>
50-
</v-btn>
51-
<v-btn
52-
v-else-if="settings.view === 'cards' && listView"
53-
icon
54-
class="action-view-list"
55-
:title="$gettext('Toggle View')"
56-
@click.stop="setView('list')"
57-
>
58-
<v-icon>mdi-view-list</v-icon>
59-
</v-btn>
60-
<v-btn
61-
v-else-if="settings.view === 'cards'"
62-
icon
63-
class="action-view-mosaic"
26+
<v-btn-toggle
27+
:model-value="settings.view"
6428
:title="$gettext('Toggle View')"
65-
@click.stop="setView('mosaic')"
29+
:density="$vuetify.display.smAndDown ? 'comfortable' : 'default'"
30+
base-color="secondary"
31+
variant="flat"
32+
rounded="pill"
33+
mandatory
34+
border
35+
group
36+
class="ms-1"
6637
>
67-
<v-icon>mdi-view-comfy</v-icon>
68-
</v-btn>
69-
<v-btn v-else icon class="action-view-cards" :title="$gettext('Toggle View')" @click.stop="setView('cards')">
70-
<v-icon>mdi-view-column</v-icon>
71-
</v-btn>
38+
<v-btn value="cards" icon="mdi-view-column" class="ps-1" @click="setView('cards')"></v-btn>
39+
<v-btn v-if="listView" value="list" icon="mdi-view-list" @click="setView('list')"></v-btn>
40+
<v-btn value="mosaic" icon="mdi-view-comfy" class="pe-1" @click="setView('mosaic')"></v-btn>
41+
</v-btn-toggle>
7242

73-
<v-btn
74-
v-if="canUpload"
75-
icon
76-
class="hidden-sm-and-down action-upload"
77-
:title="$gettext('Upload')"
78-
@click.stop="showUpload()"
79-
>
80-
<v-icon>mdi-cloud-upload</v-icon>
81-
</v-btn>
43+
<v-menu transition="slide-y-transition" open-on-click open-on-hover>
44+
<template #activator="{ props }">
45+
<v-btn density="comfortable" icon="mdi-dots-vertical" v-bind="props" class="action-menu ms-1"></v-btn>
46+
</template>
47+
48+
<v-list min-width="128" density="comfortable" bg-color="navigation" slim class="ra-8 opacity-95">
49+
<v-list-item
50+
prepend-icon="mdi-refresh"
51+
:subtitle="$gettext('Refresh')"
52+
class="action-reload action-refresh"
53+
@click="refresh"
54+
></v-list-item>
55+
<v-list-item
56+
v-if="canManage"
57+
:subtitle="$gettext('Edit')"
58+
prepend-icon="mdi-pencil"
59+
class="action-edit"
60+
@click="dialog.edit = true"
61+
></v-list-item>
62+
<v-list-item
63+
v-if="canShare"
64+
:subtitle="$gettext('Share')"
65+
prepend-icon="mdi-share-variant"
66+
class="action-share"
67+
@click="dialog.share = true"
68+
></v-list-item>
69+
<v-list-item
70+
v-if="canDownload"
71+
:subtitle="$gettext('Download')"
72+
prepend-icon="mdi-download"
73+
class="action-download"
74+
@click="download"
75+
></v-list-item>
76+
<v-list-item
77+
v-if="canUpload"
78+
:subtitle="$gettext('Upload')"
79+
prepend-icon="mdi-cloud-upload"
80+
class="action-upload"
81+
@click="showUpload"
82+
></v-list-item>
83+
</v-list>
84+
</v-menu>
8285
</v-toolbar>
8386

8487
<div v-if="album.Description" class="toolbar-details-panel">

frontend/src/component/notify.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export default {
8484
},
8585
8686
addErrorMessage: function (message) {
87-
this.addMessage("error", "alert-decagram", message, 8000);
87+
this.addMessage("error", "alert-circle-outline", message, 8000);
8888
},
8989
9090
addMessage: function (color, icon, text, delay) {

frontend/src/component/photo/delete/dialog.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
>
99
<v-card>
1010
<v-card-title class="d-flex justify-start align-center ga-3">
11-
<v-icon size="54" color="primary">mdi-delete-outline</v-icon>
11+
<v-icon :icon="icon" size="54" color="primary"></v-icon>
1212
<p v-if="text === ''" class="text-subtitle-1">
1313
{{ $gettext(`Are you sure you want to permanently delete these pictures?`) }}
1414
</p>
@@ -36,6 +36,10 @@ export default {
3636
type: Boolean,
3737
default: false,
3838
},
39+
icon: {
40+
type: String,
41+
default: "mdi-delete-outline",
42+
},
3943
text: {
4044
type: String,
4145
default: "",

frontend/src/component/photo/toolbar.vue

Lines changed: 65 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -17,109 +17,98 @@
1717
<template v-if="!embedded">
1818
<v-text-field
1919
:model-value="filter.q"
20+
:density="density"
2021
hide-details
2122
clearable
2223
single-line
2324
overflow
24-
rounded
25+
rounded="pill"
2526
variant="solo-filled"
26-
:density="density"
27+
color="surface-variant"
2728
validate-on="invalid-input"
2829
autocorrect="off"
2930
autocapitalize="none"
3031
autocomplete="off"
32+
prepend-inner-icon="mdi-tune"
33+
:append-inner-icon="filter.latlng ? 'mdi-map-marker-off' : ''"
3134
:placeholder="$gettext('Search')"
32-
prepend-inner-icon="mdi-magnify"
33-
color="surface-variant"
3435
class="input-search background-inherit elevation-0"
36+
:class="{ 'input-search--expanded': expanded }"
3537
@update:model-value="
3638
(v) => {
3739
updateFilter({ q: v });
3840
}
3941
"
4042
@keyup.enter="() => onUpdate()"
43+
@click:prepend-inner.stop="toggleExpansionPanel"
44+
@click:append-inner.stop="clearLocation"
4145
@click:clear="
4246
() => {
4347
onUpdate({ q: '' });
4448
}
4549
"
4650
></v-text-field>
4751

48-
<v-btn
49-
v-if="filter.latlng"
50-
icon
51-
:title="$gettext('Show more')"
52-
class="action-clear-location"
53-
@click.stop="clearLocation()"
54-
>
55-
<v-icon>mdi-map-marker-off</v-icon>
56-
</v-btn>
57-
58-
<v-btn icon class="hidden-xs action-reload" :title="$gettext('Reload')" @click.stop="refresh()">
59-
<v-icon>mdi-refresh</v-icon>
60-
</v-btn>
61-
62-
<v-btn
63-
v-if="settings.view === 'list'"
64-
icon
65-
class="action-view-mosaic"
66-
:title="$gettext('Toggle View')"
67-
@click.stop="setView('mosaic')"
68-
>
69-
<v-icon>mdi-view-comfy</v-icon>
70-
</v-btn>
71-
72-
<v-btn
73-
v-else-if="settings.view === 'cards' && listView"
74-
icon
75-
class="action-view-list"
76-
:title="$gettext('Toggle View')"
77-
@click.stop="setView('list')"
78-
>
79-
<v-icon>mdi-view-list</v-icon>
80-
</v-btn>
81-
82-
<v-btn
83-
v-else-if="settings.view === 'cards'"
84-
icon
85-
class="action-view-mosaic"
52+
<v-btn-toggle
53+
:model-value="settings.view"
8654
:title="$gettext('Toggle View')"
87-
@click.stop="setView('mosaic')"
55+
:density="$vuetify.display.smAndDown ? 'comfortable' : 'default'"
56+
base-color="secondary"
57+
variant="flat"
58+
rounded="pill"
59+
mandatory
60+
border
61+
group
62+
class="ms-1"
8863
>
89-
<v-icon>mdi-view-comfy</v-icon>
90-
</v-btn>
91-
92-
<v-btn v-else icon class="action-view-cards" :title="$gettext('Toggle View')" @click.stop="setView('cards')">
93-
<v-icon>mdi-view-column</v-icon>
94-
</v-btn>
64+
<v-btn value="cards" icon="mdi-view-column" class="ps-1" @click="setView('cards')"></v-btn>
65+
<v-btn v-if="listView" value="list" icon="mdi-view-list" @click="setView('list')"></v-btn>
66+
<v-btn value="mosaic" icon="mdi-view-comfy" class="pe-1" @click="setView('mosaic')"></v-btn>
67+
</v-btn-toggle>
9568

9669
<v-btn
9770
v-if="canDelete && context === 'archive' && config.count.archived > 0"
98-
icon
99-
class="action-delete-all"
10071
:title="$gettext('Delete All')"
101-
@click.stop="deleteAll()"
72+
icon="mdi-delete-sweep"
73+
class="action-delete-all ms-1"
74+
@click.stop="deleteAll"
10275
>
103-
<v-icon>mdi-delete-sweep</v-icon>
10476
</v-btn>
10577

106-
<v-btn
107-
v-else-if="canUpload"
108-
icon
109-
class="hidden-sm-and-down action-upload"
110-
:title="$gettext('Upload')"
111-
@click.stop="showUpload()"
112-
>
113-
<v-icon>mdi-cloud-upload</v-icon>
114-
</v-btn>
78+
<v-menu v-if="$vuetify.display.mdAndUp" transition="slide-y-transition" open-on-click open-on-hover>
79+
<template #activator="{ props }">
80+
<v-btn density="comfortable" icon="mdi-dots-vertical" v-bind="props" class="action-menu ms-1"></v-btn>
81+
</template>
11582

116-
<v-btn
117-
:icon="expanded ? 'mdi-chevron-up' : 'mdi-chevron-down'"
118-
:title="$gettext('Expand Search')"
119-
class="action-expand"
120-
:class="{ 'action-expand--active': expanded }"
121-
@click.stop="toggleExpansionPanel"
122-
></v-btn>
83+
<v-list min-width="128" density="comfortable" bg-color="navigation" slim class="ra-8 opacity-95">
84+
<v-list-item
85+
prepend-icon="mdi-refresh"
86+
:subtitle="$gettext('Refresh')"
87+
class="action-reload action-refresh"
88+
@click="refresh"
89+
></v-list-item>
90+
<v-list-item
91+
v-if="canUpload && context !== 'archive'"
92+
:subtitle="$gettext('Upload')"
93+
prepend-icon="mdi-cloud-upload"
94+
class="action-upload"
95+
@click="showUpload"
96+
></v-list-item>
97+
<v-list-item
98+
v-if="featSettings && isSuperAdmin"
99+
:subtitle="$gettext('Settings')"
100+
prepend-icon="mdi-film"
101+
:to="{ name: 'settings_media' }"
102+
></v-list-item>
103+
<v-list-item
104+
:subtitle="$gettext('Get Started')"
105+
prepend-icon="mdi-book-open-page-variant"
106+
href="https://docs.photoprism.app/user-guide/first-steps/"
107+
target="_blank"
108+
class="action-docs"
109+
></v-list-item>
110+
</v-list>
111+
</v-menu>
123112
</template>
124113
<template v-else>
125114
<v-spacer></v-spacer>
@@ -316,8 +305,9 @@
316305
</div>
317306
<p-photo-delete-dialog
318307
:visible="dialog.delete"
319-
:text="$gettext('Are you sure you want to delete all archived pictures?')"
320-
:action="$gettext('Delete All')"
308+
:text="$gettext(`Delete all?`)"
309+
:action="$gettext('Yes')"
310+
icon="mdi-delete-sweep-outline"
321311
@close="dialog.delete = false"
322312
@confirm="batchDelete"
323313
>
@@ -328,9 +318,11 @@
328318
import * as options from "options/options";
329319
import $api from "common/api";
330320
import $notify from "common/notify";
321+
import PConfirmAction from "../confirm/action.vue";
331322
332323
export default {
333324
name: "PPhotoToolbar",
325+
components: { PConfirmAction },
334326
props: {
335327
context: {
336328
type: String,
@@ -377,11 +369,13 @@ export default {
377369
expanded: false,
378370
experimental: this.$config.get("experimental"),
379371
isFullScreen: !!document.fullscreenElement,
372+
isSuperAdmin: this.$session.isSuperAdmin(),
380373
config: this.$config.values,
381374
readonly: readonly,
382375
canUpload: !readonly && !this.embedded && this.$config.allow("files", "upload") && features.upload,
383376
canDelete: !readonly && !this.embedded && this.$config.allow("photos", "delete") && features.delete,
384377
canAccessLibrary: this.$config.allow("photos", "access_library"),
378+
featSettings: features.settings,
385379
listView: this.$config.getSettings()?.search?.listView,
386380
all: {
387381
countries: [{ ID: "", Name: this.$gettext("All Countries") }],

0 commit comments

Comments
 (0)