enhance(client): improve clip menu ux
This commit is contained in:
parent
a7943dceca
commit
0691901345
2 changed files with 82 additions and 66 deletions
|
@ -56,7 +56,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, watch } from 'vue';
|
import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||||
import { focusPrev, focusNext } from '@/scripts/focus';
|
import { focusPrev, focusNext } from '@/scripts/focus';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu';
|
import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu';
|
||||||
|
@ -111,11 +111,11 @@ watch(() => props.items, () => {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
let childMenu = $ref<MenuItem[] | null>();
|
let childMenu = ref<MenuItem[] | null>();
|
||||||
let childTarget = $shallowRef<HTMLElement | null>();
|
let childTarget = $shallowRef<HTMLElement | null>();
|
||||||
|
|
||||||
function closeChild() {
|
function closeChild() {
|
||||||
childMenu = null;
|
childMenu.value = null;
|
||||||
childShowingItem = null;
|
childShowingItem = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,13 +140,31 @@ function onItemMouseLeave(item) {
|
||||||
if (childCloseTimer) window.clearTimeout(childCloseTimer);
|
if (childCloseTimer) window.clearTimeout(childCloseTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let childrenCache = new WeakMap();
|
||||||
async function showChildren(item: MenuItem, ev: MouseEvent) {
|
async function showChildren(item: MenuItem, ev: MouseEvent) {
|
||||||
|
const children = ref([]);
|
||||||
|
if (childrenCache.has(item)) {
|
||||||
|
children.value = childrenCache.get(item);
|
||||||
|
} else {
|
||||||
|
if (typeof item.children === 'function') {
|
||||||
|
children.value = [{
|
||||||
|
type: 'pending',
|
||||||
|
}];
|
||||||
|
item.children().then(x => {
|
||||||
|
children.value = x;
|
||||||
|
childrenCache.set(item, x);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
children.value = item.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (props.asDrawer) {
|
if (props.asDrawer) {
|
||||||
os.popupMenu(item.children, ev.currentTarget ?? ev.target);
|
os.popupMenu(children, ev.currentTarget ?? ev.target);
|
||||||
close();
|
close();
|
||||||
} else {
|
} else {
|
||||||
childTarget = ev.currentTarget ?? ev.target;
|
childTarget = ev.currentTarget ?? ev.target;
|
||||||
childMenu = item.children;
|
childMenu = children;
|
||||||
childShowingItem = item;
|
childShowingItem = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,66 +99,6 @@ export function getNoteMenu(props: {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clip(): Promise<void> {
|
|
||||||
const clips = await os.api('clips/list');
|
|
||||||
os.popupMenu([{
|
|
||||||
icon: 'ti ti-plus',
|
|
||||||
text: i18n.ts.createNew,
|
|
||||||
action: async () => {
|
|
||||||
const { canceled, result } = await os.form(i18n.ts.createNewClip, {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
label: i18n.ts.name,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
multiline: true,
|
|
||||||
label: i18n.ts.description,
|
|
||||||
},
|
|
||||||
isPublic: {
|
|
||||||
type: 'boolean',
|
|
||||||
label: i18n.ts.public,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (canceled) return;
|
|
||||||
|
|
||||||
const clip = await os.apiWithDialog('clips/create', result);
|
|
||||||
|
|
||||||
claimAchievement('noteClipped1');
|
|
||||||
os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id });
|
|
||||||
},
|
|
||||||
}, null, ...clips.map(clip => ({
|
|
||||||
text: clip.name,
|
|
||||||
action: () => {
|
|
||||||
claimAchievement('noteClipped1');
|
|
||||||
os.promiseDialog(
|
|
||||||
os.api('clips/add-note', { clipId: clip.id, noteId: appearNote.id }),
|
|
||||||
null,
|
|
||||||
async (err) => {
|
|
||||||
if (err.id === '734806c4-542c-463a-9311-15c512803965') {
|
|
||||||
const confirm = await os.confirm({
|
|
||||||
type: 'warning',
|
|
||||||
text: i18n.t('confirmToUnclipAlreadyClippedNote', { name: clip.name }),
|
|
||||||
});
|
|
||||||
if (!confirm.canceled) {
|
|
||||||
os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id });
|
|
||||||
if (props.currentClipPage?.value.id === clip.id) props.isDeleted.value = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
os.alert({
|
|
||||||
type: 'error',
|
|
||||||
text: err.message + '\n' + err.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}))], props.menuButton.value, {
|
|
||||||
}).then(focus);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function unclip(): Promise<void> {
|
async function unclip(): Promise<void> {
|
||||||
os.apiWithDialog('clips/remove-note', { clipId: props.currentClipPage.value.id, noteId: appearNote.id });
|
os.apiWithDialog('clips/remove-note', { clipId: props.currentClipPage.value.id, noteId: appearNote.id });
|
||||||
props.isDeleted.value = true;
|
props.isDeleted.value = true;
|
||||||
|
@ -264,9 +204,67 @@ export function getNoteMenu(props: {
|
||||||
action: () => toggleFavorite(true),
|
action: () => toggleFavorite(true),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
type: 'parent',
|
||||||
icon: 'ti ti-paperclip',
|
icon: 'ti ti-paperclip',
|
||||||
text: i18n.ts.clip,
|
text: i18n.ts.clip,
|
||||||
action: () => clip(),
|
children: async () => {
|
||||||
|
const clips = await os.api('clips/list');
|
||||||
|
return [{
|
||||||
|
icon: 'ti ti-plus',
|
||||||
|
text: i18n.ts.createNew,
|
||||||
|
action: async () => {
|
||||||
|
const { canceled, result } = await os.form(i18n.ts.createNewClip, {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
label: i18n.ts.name,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
multiline: true,
|
||||||
|
label: i18n.ts.description,
|
||||||
|
},
|
||||||
|
isPublic: {
|
||||||
|
type: 'boolean',
|
||||||
|
label: i18n.ts.public,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
|
const clip = await os.apiWithDialog('clips/create', result);
|
||||||
|
|
||||||
|
claimAchievement('noteClipped1');
|
||||||
|
os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id });
|
||||||
|
},
|
||||||
|
}, null, ...clips.map(clip => ({
|
||||||
|
text: clip.name,
|
||||||
|
action: () => {
|
||||||
|
claimAchievement('noteClipped1');
|
||||||
|
os.promiseDialog(
|
||||||
|
os.api('clips/add-note', { clipId: clip.id, noteId: appearNote.id }),
|
||||||
|
null,
|
||||||
|
async (err) => {
|
||||||
|
if (err.id === '734806c4-542c-463a-9311-15c512803965') {
|
||||||
|
const confirm = await os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.t('confirmToUnclipAlreadyClippedNote', { name: clip.name }),
|
||||||
|
});
|
||||||
|
if (!confirm.canceled) {
|
||||||
|
os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id });
|
||||||
|
if (props.currentClipPage?.value.id === clip.id) props.isDeleted.value = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
os.alert({
|
||||||
|
type: 'error',
|
||||||
|
text: err.message + '\n' + err.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}))];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
statePromise.then(state => state.isMutedThread ? {
|
statePromise.then(state => state.isMutedThread ? {
|
||||||
icon: 'ti ti-message-off',
|
icon: 'ti ti-message-off',
|
||||||
|
|
Loading…
Reference in a new issue