Skip to content

Commit

Permalink
Bookmark improvements (#12031)
Browse files Browse the repository at this point in the history
* add icon & color to bookmarks

* update current bookmark title on edit

* clean up edit bookmark dialog on cancel

* remove unused bookmark-edit component

* interaction improvements

* sort based on scope and alphabetically

* prevent hover when locked & use tooltip

* Reduce size of right hand icon in bookmark

Co-authored-by: rijkvanzanten <rijkvanzanten@me.com>
  • Loading branch information
azrikahar and rijkvanzanten committed Mar 28, 2022
1 parent a58f091 commit 31cfb82
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 144 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Knex } from 'knex';

export async function up(knex: Knex): Promise<void> {
await knex.schema.alterTable('directus_presets', (table) => {
table.string('icon', 30).notNullable().defaultTo('bookmark_outline');
table.string('color').nullable();
});
}

export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable('directus_presets', (table) => {
table.dropColumn('icon');
table.dropColumn('color');
});
}
6 changes: 6 additions & 0 deletions api/src/database/system-data/fields/presets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ fields:
- field: bookmark
width: half

- field: icon
width: half

- field: color
width: half

- field: search
width: half

Expand Down
5 changes: 5 additions & 0 deletions app/src/composables/use-preset/use-preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ export function usePreset(
initLocalPreset();
});

// update current bookmark title when it is edited in navigation-bookmark
presetsStore.$subscribe(() => {
initLocalPreset();
});

const layoutOptions = computed<Record<string, any>>({
get() {
return localPreset.value.layout_options?.[layout.value] || null;
Expand Down
9 changes: 8 additions & 1 deletion app/src/lang/translations/en-US.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,13 @@ delete_folder: Delete Folder
prefix: Prefix
suffix: Suffix
reset_bookmark: Reset Bookmark
rename_bookmark: Rename Bookmark
update_bookmark: Update Bookmark
delete_bookmark: Delete Bookmark
delete_bookmark_copy: >-
Are you sure you want to delete the "{bookmark}" bookmark? This action cannot be undone.
delete_personal_bookmark: Delete Personal Bookmark
delete_role_bookmark: Delete Role Bookmark
delete_global_bookmark: Delete Global Bookmark
logoutReason:
SIGN_OUT: Signed out
SESSION_EXPIRED: Session expired
Expand Down Expand Up @@ -569,6 +571,11 @@ value_hashed: Value Securely Hashed
bookmark_name: Bookmark name...
create_bookmark: Create Bookmark
edit_bookmark: Edit Bookmark
edit_personal_bookmark: Edit Personal Bookmark
edit_role_bookmark: Edit Role Bookmark
edit_global_bookmark: Edit Global Bookmark
cannot_edit_role_bookmarks: Can't Edit Role Bookmarks
cannot_edit_global_bookmarks: Can't Edit Global Bookmarks
bookmarks: Bookmarks
presets: Presets
unexpected_error: Unexpected Error
Expand Down
137 changes: 102 additions & 35 deletions app/src/modules/content/components/navigation-bookmark.vue
Original file line number Diff line number Diff line change
@@ -1,46 +1,64 @@
<template>
<v-list-item
v-context-menu="'contextMenu'"
:to="`/content/${bookmark.collection}?bookmark=${bookmark.id}`"
query
class="bookmark"
clickable
@contextmenu.stop=""
>
<v-list-item-icon><v-icon name="bookmark_outline" /></v-list-item-icon>
<v-list-item-icon><v-icon :name="bookmark.icon" :color="bookmark.color" /></v-list-item-icon>
<v-list-item-content>
<v-text-overflow :text="bookmark.bookmark" />
</v-list-item-content>

<v-menu ref="contextMenu" show-arrow placement="bottom-start">
<v-menu placement="bottom-start" show-arrow>
<template #activator="{ toggle }">
<v-icon
v-tooltip.bottom="!hasPermission && t(`cannot_edit_${scope}_bookmarks`)"
:name="hasPermission ? 'more_vert' : 'lock'"
:clickable="hasPermission"
small
class="ctx-toggle"
@click.prevent="hasPermission ? toggle() : null"
/>
</template>
<v-list>
<v-list-item clickable :disabled="isMine === false" @click="renameActive = true">
<v-list-item
clickable
:to="scope !== 'personal' ? `/settings/presets/${bookmark.id}` : undefined"
@click="scope === 'personal' ? (editActive = true) : undefined"
>
<v-list-item-icon>
<v-icon name="edit" outline />
</v-list-item-icon>
<v-list-item-content>
<v-text-overflow :text="t('rename_bookmark')" />
<v-text-overflow :text="t(`edit_${scope}_bookmark`)" />
</v-list-item-content>
</v-list-item>
<v-list-item clickable class="danger" :disabled="isMine === false" @click="deleteActive = true">
<v-list-item clickable class="danger" @click="deleteActive = true">
<v-list-item-icon>
<v-icon name="delete" outline />
</v-list-item-icon>
<v-list-item-content>
<v-text-overflow :text="t('delete_bookmark')" />
<v-text-overflow :text="t(`delete_${scope}_bookmark`)" />
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>

<v-dialog v-model="renameActive" persistent @esc="renameActive = false">
<v-dialog v-model="editActive" persistent @esc="editCancel">
<v-card>
<v-card-title>{{ t('rename_bookmark') }}</v-card-title>
<v-card-title>{{ t('edit_personal_bookmark') }}</v-card-title>
<v-card-text>
<v-input v-model="renameValue" autofocus @keyup.enter="renameSave" />
<div class="fields">
<v-input v-model="editValue.name" class="full" autofocus @keyup.enter="editSave" />
<interface-select-icon width="half" :value="editValue.icon" @input="editValue.icon = $event" />
<interface-select-color width="half" :value="editValue.color" @input="editValue.color = $event" />
</div>
</v-card-text>
<v-card-actions>
<v-button secondary @click="renameActive = false">{{ t('cancel') }}</v-button>
<v-button :disabled="renameValue === null" :loading="renameSaving" @click="renameSave">
<v-button secondary @click="editCancel">{{ t('cancel') }}</v-button>
<v-button :disabled="editValue.name === null" :loading="editSaving" @click="editSave">
{{ t('save') }}
</v-button>
</v-card-actions>
Expand All @@ -63,7 +81,7 @@

<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent, PropType, ref, computed } from 'vue';
import { defineComponent, PropType, ref, computed, reactive } from 'vue';
import { Preset } from '@directus/shared/types';
import { useUserStore, usePresetsStore } from '@/stores';
import { unexpectedError } from '@/utils/unexpected-error';
Expand All @@ -82,66 +100,88 @@ export default defineComponent({
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
const { currentUser, isAdmin } = useUserStore();
const presetsStore = usePresetsStore();
const isMine = computed(() => props.bookmark.user === userStore.currentUser!.id);
const isMine = computed(() => props.bookmark.user === currentUser!.id);
const { renameActive, renameValue, renameSave, renameSaving } = useRenameBookmark();
const { deleteActive, deleteValue, deleteSave, deleteSaving } = useDeleteBookmark();
const hasPermission = computed(() => isMine.value || isAdmin);
const scope = computed(() => {
if (props.bookmark.user && !props.bookmark.role) return 'personal';
if (!props.bookmark.user && props.bookmark.role) return 'role';
return 'global';
});
const { editActive, editValue, editSave, editSaving, editCancel } = useEditBookmark();
const { deleteActive, deleteSave, deleteSaving } = useDeleteBookmark();
return {
t,
isMine,
renameActive,
renameValue,
renameSave,
renameSaving,
hasPermission,
scope,
editActive,
editValue,
editSave,
editSaving,
editCancel,
deleteActive,
deleteValue,
deleteSave,
deleteSaving,
};
function useRenameBookmark() {
const renameActive = ref(false);
const renameValue = ref(props.bookmark.bookmark);
const renameSaving = ref(false);
function useEditBookmark() {
const editActive = ref(false);
const editValue = reactive({
name: props.bookmark.bookmark,
icon: props.bookmark?.icon ?? 'bookmark_outline',
color: props.bookmark?.color ?? null,
});
const editSaving = ref(false);
return { renameActive, renameValue, renameSave, renameSaving };
return { editActive, editValue, editSave, editSaving, editCancel };
async function renameSave() {
renameSaving.value = true;
async function editSave() {
editSaving.value = true;
try {
await presetsStore.savePreset({
...props.bookmark,
bookmark: renameValue.value,
bookmark: editValue.name,
icon: editValue.icon,
color: editValue.color,
});
renameActive.value = false;
editActive.value = false;
} catch (err: any) {
unexpectedError(err);
} finally {
renameSaving.value = false;
editSaving.value = false;
}
}
function editCancel() {
editActive.value = false;
editValue.name = props.bookmark.bookmark;
editValue.icon = props.bookmark?.icon ?? 'bookmark_outline';
editValue.color = props.bookmark?.color ?? null;
}
}
function useDeleteBookmark() {
const deleteActive = ref(false);
const deleteValue = ref(props.bookmark.bookmark);
const deleteSaving = ref(false);
return { deleteActive, deleteValue, deleteSave, deleteSaving };
return { deleteActive, deleteSave, deleteSaving };
async function deleteSave() {
deleteSaving.value = true;
try {
let navigateTo: string | null = null;
if (+route.query?.bookmark === props.bookmark.id) {
if (route.query?.bookmark && +route.query.bookmark === props.bookmark.id) {
navigateTo = `/content/${props.bookmark.collection}`;
}
Expand All @@ -167,4 +207,31 @@ export default defineComponent({
--v-list-item-color: var(--danger);
--v-list-item-icon-color: var(--danger);
}
.v-list-item {
.ctx-toggle {
--v-icon-color: var(--foreground-subdued);
opacity: 0;
user-select: none;
transition: opacity var(--fast) var(--transition);
}
&:hover {
.ctx-toggle {
opacity: 1;
user-select: auto;
}
}
}
.fields {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
.full {
grid-column: 1 / span 2;
}
}
</style>
22 changes: 7 additions & 15 deletions app/src/modules/content/routes/collection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,6 @@ import RefreshSidebarDetail from '@/views/private/components/refresh-sidebar-det
import ExportSidebarDetail from '@/views/private/components/export-sidebar-detail.vue';
import SearchInput from '@/views/private/components/search-input';
import BookmarkAdd from '@/views/private/components/bookmark-add';
import BookmarkEdit from '@/views/private/components/bookmark-edit';
import { useRouter } from 'vue-router';
import { usePermissionsStore, useUserStore } from '@/stores';
import DrawerBatch from '@/views/private/components/drawer-batch';
Expand All @@ -303,7 +302,6 @@ export default defineComponent({
LayoutSidebarDetail,
SearchInput,
BookmarkAdd,
BookmarkEdit,
DrawerBatch,
ArchiveSidebarDetail,
RefreshSidebarDetail,
Expand Down Expand Up @@ -372,7 +370,7 @@ export default defineComponent({
batchEditActive,
} = useBatch();
const { bookmarkDialogActive, creatingBookmark, createBookmark, editingBookmark, editBookmark } = useBookmarks();
const { bookmarkDialogActive, creatingBookmark, createBookmark } = useBookmarks();
const currentLayout = computed(() => layouts.value.find((l) => l.id === layout.value));
Expand Down Expand Up @@ -447,8 +445,6 @@ export default defineComponent({
creatingBookmark,
createBookmark,
bookmarkTitle,
editingBookmark,
editBookmark,
breadcrumb,
clearFilters,
confirmArchive,
Expand Down Expand Up @@ -583,21 +579,22 @@ export default defineComponent({
function useBookmarks() {
const bookmarkDialogActive = ref(false);
const creatingBookmark = ref(false);
const editingBookmark = ref(false);
return {
bookmarkDialogActive,
creatingBookmark,
createBookmark,
editingBookmark,
editBookmark,
};
async function createBookmark(name: string) {
async function createBookmark(bookmark: any) {
creatingBookmark.value = true;
try {
const newBookmark = await saveCurrentAsBookmark({ bookmark: name });
const newBookmark = await saveCurrentAsBookmark({
bookmark: bookmark.name,
icon: bookmark.icon,
color: bookmark.color,
});
router.push(`/content/${newBookmark.collection}?bookmark=${newBookmark.id}`);
bookmarkDialogActive.value = false;
Expand All @@ -607,11 +604,6 @@ export default defineComponent({
creatingBookmark.value = false;
}
}
async function editBookmark(name: string) {
bookmarkTitle.value = name;
bookmarkDialogActive.value = false;
}
}
function clearFilters() {
Expand Down
Loading

0 comments on commit 31cfb82

Please sign in to comment.