summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkMenu.vue
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2024-07-12 16:25:44 +0900
committerGitHub <noreply@github.com>2024-07-12 16:25:44 +0900
commit385969e9f56a39a1e5547b94901d155e1e811263 (patch)
treec5c4082d99d97f3220efd4dfd0926b4e809cfd3b /packages/frontend/src/components/MkMenu.vue
parentenhance(frontend): 未使用のサウンド設定を削除 (#14116) (diff)
downloadmisskey-385969e9f56a39a1e5547b94901d155e1e811263.tar.gz
misskey-385969e9f56a39a1e5547b94901d155e1e811263.tar.bz2
misskey-385969e9f56a39a1e5547b94901d155e1e811263.zip
fix(frontend): フォーカスの挙動を修正 (#14158)
* fix(frontend): 直前のパターンを記録するように * fix(frontend): フォーカス/タブ移動に関する挙動を調整 (#226) Cherry-pick commit e8c030673326871edf3623cf2b8675d68f9e1b13 Co-authored-by: taiyme <53635909+taiyme@users.noreply.github.com> * focusのデザイン修正 * move scripts * Modalにfocus trapを追加 * 記録するホットキーはレートリミット式にする * escキーのハンドリングをMkModalに統一 * fix * enterで子メニューを開けるように * lint * fix focus trap * improve switch accessibility * 一部のmodalのフォーカストラップが外れない問題を修正 * fix * fix * Revert "記録するホットキーはレートリミット式にする" This reverts commit 40a7509286a87911ad4cc06d9482e8a2e5d0e7e8. * Revert "fix(frontend): 直前のパターンを記録するように" This reverts commit 5372b2594023952cff34aa62253ed4efef15b5dd. * Revert "Revert "fix(frontend): 直前のパターンを記録するように"" This reverts commit a9bb52e799e110927ad92cd8f26af980819334e1. * Revert "Revert "記録するホットキーはレートリミット式にする"" This reverts commit bdac34273e0bc5f13604c7e2f9fa6b1321a0df3d. * 試験的にCypressでのFocustrapを無効化 * fix * fix focus-trap * Update Changelog * :v: * fix focustrap invocation logic * スクロールがsticky headerを考慮するように * :art: * スタイルの微調整 * :art: * remove deprecated key aliases * focusElementが足りなかったので修正 * preview系にfocus時スタイルが足りなかったので修正 * `returnFocusElement` -> `returnFocusTo` * lint * Update packages/frontend/src/components/MkModalWindow.vue * Apply suggestions from code review Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com> * keydownイベントをまとめる * use correct pesudo-element selector * fix * rename --------- Co-authored-by: taiyme <53635909+taiyme@users.noreply.github.com> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/frontend/src/components/MkMenu.vue')
-rw-r--r--packages/frontend/src/components/MkMenu.vue345
1 files changed, 218 insertions, 127 deletions
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 119504f744..68479989b2 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -4,23 +4,42 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div role="menu">
+<div role="menu" @focusin.passive.stop="() => {}">
<div
- ref="itemsEl" v-hotkey="keymap"
+ ref="itemsEl"
+ v-hotkey="keymap"
+ tabindex="0"
class="_popup _shadow"
- :class="[$style.root, { [$style.center]: align === 'center', [$style.asDrawer]: asDrawer }]"
- :style="{ width: (width && !asDrawer) ? width + 'px' : '', maxHeight: maxHeight ? maxHeight + 'px' : '' }"
- @contextmenu.self="e => e.preventDefault()"
+ :class="{
+ [$style.root]: true,
+ [$style.center]: align === 'center',
+ [$style.asDrawer]: asDrawer,
+ }"
+ :style="{
+ width: (width && !asDrawer) ? `${width}px` : '',
+ maxHeight: maxHeight ? `${maxHeight}px` : '',
+ }"
+ @keydown.stop="() => {}"
+ @contextmenu.self.prevent="() => {}"
>
- <template v-for="(item, i) in (items2 ?? [])">
- <div v-if="item.type === 'divider'" role="separator" :class="$style.divider"></div>
- <span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item]">
+ <template v-for="item in (items2 ?? [])">
+ <div v-if="item.type === 'divider'" role="separator" tabindex="-1" :class="$style.divider"></div>
+ <span v-else-if="item.type === 'label'" role="menuitem" tabindex="-1" :class="[$style.label, $style.item]">
<span style="opacity: 0.7;">{{ item.text }}</span>
</span>
- <span v-else-if="item.type === 'pending'" role="menuitem" :tabindex="i" :class="[$style.pending, $style.item]">
+ <span v-else-if="item.type === 'pending'" role="menuitem" tabindex="0" :class="[$style.pending, $style.item]">
<span><MkEllipsis/></span>
</span>
- <MkA v-else-if="item.type === 'link'" role="menuitem" :to="item.to" :tabindex="i" class="_button" :class="$style.item" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+ <MkA
+ v-else-if="item.type === 'link'"
+ role="menuitem"
+ tabindex="0"
+ :class="['_button', $style.item]"
+ :to="item.to"
+ @click.passive="close(true)"
+ @mouseenter.passive="onItemMouseEnter"
+ @mouseleave.passive="onItemMouseLeave"
+ >
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
<MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/>
<div :class="$style.item_content">
@@ -28,20 +47,48 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
</div>
</MkA>
- <a v-else-if="item.type === 'a'" role="menuitem" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="$style.item" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+ <a
+ v-else-if="item.type === 'a'"
+ role="menuitem"
+ tabindex="0"
+ :class="['_button', $style.item]"
+ :href="item.href"
+ :target="item.target"
+ :download="item.download"
+ @click.passive="close(true)"
+ @mouseenter.passive="onItemMouseEnter"
+ @mouseleave.passive="onItemMouseLeave"
+ >
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
<div :class="$style.item_content">
<span :class="$style.item_content_text">{{ item.text }}</span>
<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
</div>
</a>
- <button v-else-if="item.type === 'user'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+ <button
+ v-else-if="item.type === 'user'"
+ role="menuitem"
+ tabindex="0"
+ :class="['_button', $style.item, { [$style.active]: item.active }]"
+ @click.prevent="item.active ? close(false) : clicked(item.action, $event)"
+ @mouseenter.passive="onItemMouseEnter"
+ @mouseleave.passive="onItemMouseLeave"
+ >
<MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/>
<div v-if="item.indicate" :class="$style.item_content">
<span :class="$style.indicator"><i class="_indicatorCircle"></i></span>
</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)">
+ <button
+ v-else-if="item.type === 'switch'"
+ role="menuitemcheckbox"
+ tabindex="0"
+ :class="['_button', $style.item]"
+ :disabled="unref(item.disabled)"
+ @click.prevent="switchItem(item)"
+ @mouseenter.passive="onItemMouseEnter"
+ @mouseleave.passive="onItemMouseLeave"
+ >
<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">
@@ -49,29 +96,61 @@ SPDX-License-Identifier: AGPL-3.0-only
<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)">
+ <button
+ v-else-if="item.type === 'radio'"
+ role="menuitem"
+ tabindex="0"
+ :class="['_button', $style.item, $style.parent, { [$style.active]: childShowingItem === item }]"
+ :disabled="unref(item.disabled)"
+ @mouseenter.prevent="preferClick ? null : showRadioOptions(item, $event)"
+ @keydown.enter.prevent="preferClick ? null : showRadioOptions(item, $event)"
+ @click.prevent="!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)">
+ <button
+ v-else-if="item.type === 'radioOption'"
+ role="menuitemradio"
+ tabindex="0"
+ :class="['_button', $style.item, $style.radio, { [$style.active]: unref(item.active) }]"
+ @click.prevent="unref(item.active) ? null : clicked(item.action, $event, false)"
+ @mouseenter.passive="onItemMouseEnter"
+ @mouseleave.passive="onItemMouseLeave"
+ >
<div :class="$style.icon">
- <span :class="[$style.radio, { [$style.radioChecked]: item.active }]"></span>
+ <span :class="[$style.radioIcon, { [$style.radioChecked]: unref(item.active) }]"></span>
</div>
<div :class="$style.item_content">
<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)">
+ <button
+ v-else-if="item.type === 'parent'"
+ role="menuitem"
+ tabindex="0"
+ :class="['_button', $style.item, $style.parent, { [$style.active]: childShowingItem === item }]"
+ @mouseenter.prevent="preferClick ? null : showChildren(item, $event)"
+ @keydown.enter.prevent="preferClick ? null : showChildren(item, $event)"
+ @click.prevent="!preferClick ? null : showChildren(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 :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: getValue(item.active) }]" :disabled="getValue(item.active)" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+ <button
+ v-else role="menuitem"
+ tabindex="0"
+ :class="['_button', $style.item, { [$style.danger]: item.danger, [$style.active]: unref(item.active) }]"
+ @click.prevent="unref(item.active) ? close(false) : clicked(item.action, $event)"
+ @mouseenter.passive="onItemMouseEnter"
+ @mouseleave.passive="onItemMouseLeave"
+ >
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
<MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/>
<div :class="$style.item_content">
@@ -80,25 +159,26 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</button>
</template>
- <span v-if="items2 == null || items2.length === 0" :class="[$style.none, $style.item]">
+ <span v-if="items2 == null || items2.length === 0" tabindex="-1" :class="[$style.none, $style.item]">
<span>{{ i18n.ts.none }}</span>
</span>
</div>
<div v-if="childMenu">
- <XChild ref="child" :items="childMenu" :targetElement="childTarget!" :rootElement="itemsEl!" showing @actioned="childActioned" @close="close(false)"/>
+ <XChild ref="child" :items="childMenu" :targetElement="childTarget!" :rootElement="itemsEl!" @actioned="childActioned" @closed="closeChild"/>
</div>
</div>
</template>
<script lang="ts">
-import { ComputedRef, computed, defineAsyncComponent, isRef, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
-import { focusPrev, focusNext } from '@/scripts/focus.js';
+import { computed, defineAsyncComponent, inject, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, unref, watch } from 'vue';
import MkSwitchButton from '@/components/MkSwitch.button.vue';
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';
import { type Keymap } from '@/scripts/hotkey.js';
+import { isFocusable } from '@/scripts/focus.js';
+import { getNodeOrNull } from '@/scripts/get-dom-node-or-null.js';
const childrenCache = new WeakMap<MenuParent, MenuItem[]>();
</script>
@@ -108,7 +188,6 @@ const XChild = defineAsyncComponent(() => import('./MkMenu.child.vue'));
const props = defineProps<{
items: MenuItem[];
- viaKeyboard?: boolean;
asDrawer?: boolean;
align?: 'center' | string;
width?: number;
@@ -120,7 +199,9 @@ const emit = defineEmits<{
(ev: 'hide'): void;
}>();
-const itemsEl = shallowRef<HTMLDivElement>();
+const isNestingMenu = inject<boolean>('isNestingMenu', false);
+
+const itemsEl = shallowRef<HTMLElement>();
const items2 = ref<InnerMenuItem[]>();
@@ -177,25 +258,19 @@ function childActioned() {
close(true);
}
-const onGlobalMousedown = (event: MouseEvent) => {
- if (childTarget.value && (event.target === childTarget.value || childTarget.value.contains(event.target as Node))) return;
- if (child.value && child.value.checkHit(event)) return;
- closeChild();
-};
-
let childCloseTimer: null | number = null;
-function onItemMouseEnter(item) {
+function onItemMouseEnter() {
childCloseTimer = window.setTimeout(() => {
closeChild();
}, 300);
}
-function onItemMouseLeave(item) {
+function onItemMouseLeave() {
if (childCloseTimer) window.clearTimeout(childCloseTimer);
}
-async function showRadioOptions(item: MenuRadio, ev: MouseEvent) {
+async function showRadioOptions(item: MenuRadio, ev: Event) {
const children: MenuItem[] = Object.keys(item.options).map<MenuRadioOption>(key => {
const value = item.options[key];
return {
@@ -210,7 +285,7 @@ async function showRadioOptions(item: MenuRadio, ev: MouseEvent) {
if (props.asDrawer) {
os.popupMenu(children, ev.currentTarget ?? ev.target).finally(() => {
- emit('close');
+ close(false);
});
emit('hide');
} else {
@@ -220,7 +295,7 @@ async function showRadioOptions(item: MenuRadio, ev: MouseEvent) {
}
}
-async function showChildren(item: MenuParent, ev: MouseEvent) {
+async function showChildren(item: MenuParent, ev: Event) {
const children: MenuItem[] = await (async () => {
if (childrenCache.has(item)) {
return childrenCache.get(item)!;
@@ -237,7 +312,7 @@ async function showChildren(item: MenuParent, ev: MouseEvent) {
if (props.asDrawer) {
os.popupMenu(children, ev.currentTarget ?? ev.target).finally(() => {
- emit('close');
+ close(false);
});
emit('hide');
} else {
@@ -256,41 +331,87 @@ function clicked(fn: MenuAction, ev: MouseEvent, doClose = true) {
}
function close(actioned = false) {
- emit('close', actioned);
+ disposeHandlers();
+ nextTick(() => {
+ closeChild();
+ emit('close', actioned);
+ });
+}
+
+function switchItem(item: MenuSwitch & { ref: any }) {
+ if (item.disabled !== undefined && (typeof item.disabled === 'boolean' ? item.disabled : item.disabled.value)) return;
+ item.ref = !item.ref;
}
function focusUp() {
- focusPrev(document.activeElement);
+ if (disposed) return;
+ if (!itemsEl.value?.contains(document.activeElement)) return;
+
+ const focusableElements = Array.from(itemsEl.value.children).filter(isFocusable);
+ const activeIndex = focusableElements.findIndex(el => el === document.activeElement);
+ const targetIndex = (activeIndex !== -1 && activeIndex !== 0) ? (activeIndex - 1) : (focusableElements.length - 1);
+ const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value;
+
+ targetElement.focus();
}
function focusDown() {
- focusNext(document.activeElement);
-}
+ if (disposed) return;
+ if (!itemsEl.value?.contains(document.activeElement)) return;
-function switchItem(item: MenuSwitch & { ref: any }) {
- if (item.disabled !== undefined && (typeof item.disabled === 'boolean' ? item.disabled : item.disabled.value)) return;
- item.ref = !item.ref;
-}
+ const focusableElements = Array.from(itemsEl.value.children).filter(isFocusable);
+ const activeIndex = focusableElements.findIndex(el => el === document.activeElement);
+ const targetIndex = (activeIndex !== -1 && activeIndex !== (focusableElements.length - 1)) ? (activeIndex + 1) : 0;
+ const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value;
-function getValue<T>(item?: ComputedRef<T> | T) {
- return isRef(item) ? item.value : item;
+ targetElement.focus();
}
-onMounted(() => {
- if (props.viaKeyboard) {
- nextTick(() => {
- if (itemsEl.value) focusNext(itemsEl.value.children[0], true, false);
- });
- }
+const onGlobalFocusin = (ev: FocusEvent) => {
+ if (disposed) return;
+ if (itemsEl.value?.parentElement?.contains(getNodeOrNull(ev.target))) return;
+ nextTick(() => {
+ if (itemsEl.value != null && isFocusable(itemsEl.value)) {
+ itemsEl.value.focus({ preventScroll: true });
+ nextTick(() => focusDown());
+ }
+ });
+};
- // TODO: アクティブな要素までスクロール
- //itemsEl.scrollTo();
+const onGlobalMousedown = (ev: MouseEvent) => {
+ if (disposed) return;
+ if (childTarget.value?.contains(getNodeOrNull(ev.target))) return;
+ if (child.value?.checkHit(ev)) return;
+ closeChild();
+};
+const setupHandlers = () => {
+ if (!isNestingMenu) {
+ document.addEventListener('focusin', onGlobalFocusin, { passive: true });
+ }
document.addEventListener('mousedown', onGlobalMousedown, { passive: true });
+};
+
+let disposed = false;
+
+const disposeHandlers = () => {
+ disposed = true;
+ if (!isNestingMenu) {
+ document.removeEventListener('focusin', onGlobalFocusin);
+ }
+ document.removeEventListener('mousedown', onGlobalMousedown);
+};
+
+onMounted(() => {
+ setupHandlers();
+
+ if (!isNestingMenu) {
+ nextTick(() => itemsEl.value?.focus({ preventScroll: true }));
+ }
});
onBeforeUnmount(() => {
- document.removeEventListener('mousedown', onGlobalMousedown);
+ disposeHandlers();
});
</script>
@@ -303,6 +424,10 @@ onBeforeUnmount(() => {
overflow: auto;
overscroll-behavior: contain;
+ &:focus-visible {
+ outline: none;
+ }
+
&.center {
> .item {
text-align: center;
@@ -320,7 +445,7 @@ onBeforeUnmount(() => {
font-size: 1em;
padding: 12px 24px;
- &:before {
+ &::before {
width: calc(100% - 24px);
border-radius: 12px;
}
@@ -350,8 +475,10 @@ onBeforeUnmount(() => {
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
+ text-decoration: none !important;
+ color: var(--menuFg, var(--fg));
- &:before {
+ &::before {
content: "";
display: block;
position: absolute;
@@ -365,56 +492,56 @@ onBeforeUnmount(() => {
border-radius: 6px;
}
- &:not(:disabled):hover {
- color: var(--accent);
- text-decoration: none;
+ &:focus-visible {
+ outline: none;
- &:before {
- background: var(--accentedBg);
+ &:not(:hover):not(:active)::before {
+ outline: var(--focus) solid 2px;
+ outline-offset: -2px;
}
}
- &.danger {
- color: #ff2a2a;
-
- &:hover {
- color: #fff;
+ &:not(:disabled) {
+ &:hover,
+ &:focus-visible:active,
+ &:focus-visible.active {
+ color: var(--menuHoverFg, var(--accent));
- &:before {
- background: #ff4242;
+ &::before {
+ background-color: var(--menuHoverBg, var(--accentedBg));
}
}
- &:active {
- color: #fff;
+ &:not(:focus-visible):active,
+ &:not(:focus-visible).active {
+ color: var(--menuActiveFg, var(--fgOnAccent));
- &:before {
- background: #d42e2e !important;
+ &::before {
+ background-color: var(--menuActiveBg, var(--accent));
}
}
}
- &:active,
- &.active {
- color: var(--fgOnAccent) !important;
- opacity: 1;
-
- &:before {
- background: var(--accent) !important;
- }
+ &:disabled {
+ cursor: not-allowed;
}
- &.radioActive {
- color: var(--accent) !important;
- opacity: 1;
+ &.danger {
+ --menuFg: #ff2a2a;
+ --menuHoverFg: #fff;
+ --menuHoverBg: #ff4242;
+ --menuActiveFg: #fff;
+ --menuActiveBg: #d42e2e;
+ }
- &:before {
- background-color: var(--accentedBg) !important;
- }
+ &.radio {
+ --menuActiveFg: var(--accent);
+ --menuActiveBg: var(--accentedBg);
}
- &:not(:active):focus-visible {
- box-shadow: 0 0 0 2px var(--focus) inset;
+ &.parent {
+ --menuActiveFg: var(--accent);
+ --menuActiveBg: var(--accentedBg);
}
&.label {
@@ -432,22 +559,6 @@ onBeforeUnmount(() => {
pointer-events: none;
opacity: 0.7;
}
-
- &.parent {
- pointer-events: auto;
- display: flex;
- align-items: center;
- cursor: default;
-
- &.childShowing {
- color: var(--accent);
- text-decoration: none;
-
- &:before {
- background: var(--accentedBg);
- }
- }
- }
}
.item_content {
@@ -466,18 +577,6 @@ onBeforeUnmount(() => {
overflow: hidden;
}
-.switch {
- position: relative;
- display: flex;
- transition: all 0.2s ease;
- user-select: none;
- cursor: pointer;
-}
-
-.switchDisabled {
- cursor: not-allowed;
-}
-
.switchButton {
margin-left: -2px;
--height: 1.35em;
@@ -489,14 +588,6 @@ onBeforeUnmount(() => {
text-overflow: ellipsis;
}
-.switchInput {
- position: absolute;
- width: 0;
- height: 0;
- opacity: 0;
- margin: 0;
-}
-
.icon {
margin-right: 8px;
line-height: 1;
@@ -525,12 +616,12 @@ onBeforeUnmount(() => {
border-top: solid 0.5px var(--divider);
}
-.radio {
+.radioIcon {
display: inline-block;
position: relative;
width: 1em;
height: 1em;
- vertical-align: -.125em;
+ vertical-align: -0.125em;
border-radius: 50%;
border: solid 2px var(--divider);
background-color: var(--panel);