summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkMenu.vue
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2024-03-30 16:02:03 +0900
committerGitHub <noreply@github.com>2024-03-30 16:02:03 +0900
commitb96d9c6973b1c861306fdb9f51256cee5325a2b1 (patch)
treeb6aba03fba4f018e35213da755d4064a602bb1d6 /packages/frontend/src/components/MkMenu.vue
parentenhance(frontend): 2要素認証セットアップウィザードにアプリ... (diff)
downloadsharkey-b96d9c6973b1c861306fdb9f51256cee5325a2b1.tar.gz
sharkey-b96d9c6973b1c861306fdb9f51256cee5325a2b1.tar.bz2
sharkey-b96d9c6973b1c861306fdb9f51256cee5325a2b1.zip
fix/enhance(frontend): 映像・音声周りの改修 (#13206)
* enhance(frontend): 映像・音声周りの改修 * fix * fix design * fix lint * キーボードショートカットを整備 * Update Changelog * fix * feat: ループ再生 * ネイティブの動作と同期されるように * Update Changelog * key指定を消す
Diffstat (limited to 'packages/frontend/src/components/MkMenu.vue')
-rw-r--r--packages/frontend/src/components/MkMenu.vue91
1 files changed, 86 insertions, 5 deletions
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index faed6416d0..d91239b9e2 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -42,9 +42,26 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</button>
<button v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" class="_button" :class="[$style.item, $style.switch, { [$style.switchDisabled]: item.disabled } ]" @click="switchItem(item)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
- <MkSwitchButton :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/>
+ <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
+ <MkSwitchButton v-else :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/>
+ <div :class="$style.item_content">
+ <span :class="[$style.item_content_text, { [$style.switchText]: !item.icon }]">{{ item.text }}</span>
+ <MkSwitchButton v-if="item.icon" :class="[$style.switchButton, $style.caret]" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/>
+ </div>
+ </button>
+ <button v-else-if="item.type === 'radio'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showRadioOptions(item, $event)" @click="!preferClick ? null : showRadioOptions(item, $event)">
+ <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]" style="pointer-events: none;"></i>
+ <div :class="$style.item_content">
+ <span :class="$style.item_content_text" style="pointer-events: none;">{{ item.text }}</span>
+ <span :class="$style.caret" style="pointer-events: none;"><i class="ti ti-chevron-right ti-fw"></i></span>
+ </div>
+ </button>
+ <button v-else-if="item.type === 'radioOption'" :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.radioActive]: item.active }]" @click="clicked(item.action, $event, false)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+ <div :class="$style.icon">
+ <span :class="[$style.radio, { [$style.radioChecked]: item.active }]"></span>
+ </div>
<div :class="$style.item_content">
- <span :class="[$style.item_content_text, $style.switchText]">{{ item.text }}</span>
+ <span :class="$style.item_content_text">{{ item.text }}</span>
</div>
</button>
<button v-else-if="item.type === 'parent'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)">
@@ -77,7 +94,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ComputedRef, computed, defineAsyncComponent, isRef, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
import { focusPrev, focusNext } from '@/scripts/focus.js';
import MkSwitchButton from '@/components/MkSwitch.button.vue';
-import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu.js';
+import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuRadio, MenuRadioOption, MenuParent } from '@/types/menu.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { isTouchUsing } from '@/scripts/touch.js';
@@ -168,6 +185,31 @@ function onItemMouseLeave(item) {
if (childCloseTimer) window.clearTimeout(childCloseTimer);
}
+async function showRadioOptions(item: MenuRadio, ev: MouseEvent) {
+ const children: MenuItem[] = Object.keys(item.options).map<MenuRadioOption>(key => {
+ const value = item.options[key];
+ return {
+ type: 'radioOption',
+ text: key,
+ action: () => {
+ item.ref = value;
+ },
+ active: computed(() => item.ref === value),
+ };
+ });
+
+ if (props.asDrawer) {
+ os.popupMenu(children, ev.currentTarget ?? ev.target).finally(() => {
+ emit('close');
+ });
+ emit('hide');
+ } else {
+ childTarget.value = (ev.currentTarget ?? ev.target) as HTMLElement;
+ childMenu.value = children;
+ childShowingItem.value = item;
+ }
+}
+
async function showChildren(item: MenuParent, ev: MouseEvent) {
const children: MenuItem[] = await (async () => {
if (childrenCache.has(item)) {
@@ -196,8 +238,10 @@ async function showChildren(item: MenuParent, ev: MouseEvent) {
}
}
-function clicked(fn: MenuAction, ev: MouseEvent) {
+function clicked(fn: MenuAction, ev: MouseEvent, doClose = true) {
fn(ev);
+
+ if (!doClose) return;
close(true);
}
@@ -350,6 +394,15 @@ onBeforeUnmount(() => {
}
}
+ &.radioActive {
+ color: var(--accent) !important;
+ opacity: 1;
+
+ &:before {
+ background-color: var(--accentedBg) !important;
+ }
+ }
+
&:not(:active):focus-visible {
box-shadow: 0 0 0 2px var(--focus) inset;
}
@@ -417,11 +470,11 @@ onBeforeUnmount(() => {
.switchButton {
margin-left: -2px;
+ --height: 1.35em;
}
.switchText {
margin-left: 8px;
- margin-top: 2px;
overflow: hidden;
text-overflow: ellipsis;
}
@@ -461,4 +514,32 @@ onBeforeUnmount(() => {
margin: 8px 0;
border-top: solid 0.5px var(--divider);
}
+
+.radio {
+ display: inline-block;
+ position: relative;
+ width: 1em;
+ height: 1em;
+ vertical-align: -.125em;
+ border-radius: 50%;
+ border: solid 2px var(--divider);
+ background-color: var(--panel);
+
+ &.radioChecked {
+ border-color: var(--accent);
+
+ &::after {
+ content: "";
+ display: block;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 50%;
+ height: 50%;
+ border-radius: 50%;
+ background-color: var(--accent);
+ }
+ }
+}
</style>