enhance(client): improve clip menu ux

This commit is contained in:
syuilo 2023-02-24 20:48:19 +09:00
parent a7943dceca
commit 0691901345
2 changed files with 82 additions and 66 deletions

View file

@ -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;
} }
} }

View file

@ -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',