メニューのトグルをいい感じにする
This commit is contained in:
parent
61c9b0757e
commit
62249403cf
|
@ -35,9 +35,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/>
|
<MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/>
|
||||||
<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
|
<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
|
||||||
</button>
|
</button>
|
||||||
<span v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" :class="$style.item" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
<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)">
|
||||||
<MkSwitch v-model="item.ref" :disabled="item.disabled" class="form-switch">{{ item.text }}</MkSwitch>
|
<MkSwitchButton :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)" :class="$style.switchButton" />
|
||||||
</span>
|
<span :class="$style.switchText">{{ item.text }}</span>
|
||||||
|
</button>
|
||||||
<button v-else-if="item.type === 'parent'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="showChildren(item, $event)">
|
<button v-else-if="item.type === 'parent'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="showChildren(item, $event)">
|
||||||
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
|
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
|
||||||
<span>{{ item.text }}</span>
|
<span>{{ item.text }}</span>
|
||||||
|
@ -63,8 +64,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, 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 MkSwitchButton from '@/components/MkSwitch.button.vue';
|
||||||
import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu';
|
import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch } from '@/types/menu';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
|
@ -191,6 +192,11 @@ function focusDown() {
|
||||||
focusNext(document.activeElement);
|
focusNext(document.activeElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function switchItem(item: any) {
|
||||||
|
if (item.disabled) return;
|
||||||
|
item.ref = !item.ref;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.viaKeyboard) {
|
if (props.viaKeyboard) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
@ -357,6 +363,37 @@ onBeforeUnmount(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switchDisabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switchButton {
|
||||||
|
margin-left: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switchText {
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-top: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switchInput {
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span
|
||||||
|
v-tooltip="checked ? i18n.ts.itsOn : i18n.ts.itsOff"
|
||||||
|
:class="{
|
||||||
|
[$style.button]: true,
|
||||||
|
[$style.buttonChecked]: checked,
|
||||||
|
[$style.buttonDisabled]: props.disabled
|
||||||
|
}"
|
||||||
|
data-cy-switch-toggle
|
||||||
|
@click.prevent="toggle"
|
||||||
|
>
|
||||||
|
<div :class="{ [$style.knob]: true, [$style.knobChecked]: checked }"></div>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { toRefs, Ref } from 'vue';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
checked: boolean | Ref<boolean>;
|
||||||
|
disabled?: boolean;
|
||||||
|
}>(), {
|
||||||
|
disabled: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(ev: 'toggle'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const checked = toRefs(props).checked;
|
||||||
|
const toggle = () => {
|
||||||
|
emit('toggle');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.button {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 32px;
|
||||||
|
height: 23px;
|
||||||
|
outline: none;
|
||||||
|
background: var(--switchOffBg);
|
||||||
|
background-clip: content-box;
|
||||||
|
border: solid 1px var(--switchOffBg);
|
||||||
|
border-radius: 999px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: inherit;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonChecked {
|
||||||
|
background-color: var(--switchOnBg) !important;
|
||||||
|
border-color: var(--switchOnBg) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonDisabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knob {
|
||||||
|
position: absolute;
|
||||||
|
top: 3px;
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 999px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:not(.knobChecked) {
|
||||||
|
left: 3px;
|
||||||
|
background: var(--switchOffFg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.knobChecked {
|
||||||
|
left: 12px;
|
||||||
|
background: var(--switchOnFg);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -12,9 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
:class="$style.input"
|
:class="$style.input"
|
||||||
@keydown.enter="toggle"
|
@keydown.enter="toggle"
|
||||||
>
|
>
|
||||||
<span ref="button" v-tooltip="checked ? i18n.ts.itsOn : i18n.ts.itsOff" :class="$style.button" data-cy-switch-toggle @click.prevent="toggle">
|
<XButton :checked="checked" :disabled="disabled" @toggle="toggle" />
|
||||||
<div :class="$style.knob"></div>
|
|
||||||
</span>
|
|
||||||
<span :class="$style.body">
|
<span :class="$style.body">
|
||||||
<!-- TODO: 無名slotの方は廃止 -->
|
<!-- TODO: 無名slotの方は廃止 -->
|
||||||
<span :class="$style.label" @click="toggle"><slot name="label"></slot><slot></slot></span>
|
<span :class="$style.label" @click="toggle"><slot name="label"></slot><slot></slot></span>
|
||||||
|
@ -25,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toRefs, Ref } from 'vue';
|
import { toRefs, Ref } from 'vue';
|
||||||
import { i18n } from '@/i18n';
|
import XButton from '@/components/MkSwitch.button.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: boolean | Ref<boolean>;
|
modelValue: boolean | Ref<boolean>;
|
||||||
|
@ -36,7 +34,6 @@ const emit = defineEmits<{
|
||||||
(ev: 'update:modelValue', v: boolean): void;
|
(ev: 'update:modelValue', v: boolean): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let button = $shallowRef<HTMLElement>();
|
|
||||||
const checked = toRefs(props).modelValue;
|
const checked = toRefs(props).modelValue;
|
||||||
const toggle = () => {
|
const toggle = () => {
|
||||||
if (props.disabled) return;
|
if (props.disabled) return;
|
||||||
|
@ -66,17 +63,8 @@ const toggle = () => {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.checked {
|
//&.checked {
|
||||||
> .button {
|
//}
|
||||||
background-color: var(--switchOnBg) !important;
|
|
||||||
border-color: var(--switchOnBg) !important;
|
|
||||||
|
|
||||||
> .knob {
|
|
||||||
left: 12px;
|
|
||||||
background: var(--switchOnFg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
|
@ -86,36 +74,6 @@ const toggle = () => {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
|
||||||
position: relative;
|
|
||||||
display: inline-flex;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 32px;
|
|
||||||
height: 23px;
|
|
||||||
outline: none;
|
|
||||||
background: var(--switchOffBg);
|
|
||||||
background-clip: content-box;
|
|
||||||
border: solid 1px var(--switchOffBg);
|
|
||||||
border-radius: 999px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: inherit;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.knob {
|
|
||||||
position: absolute;
|
|
||||||
top: 3px;
|
|
||||||
left: 3px;
|
|
||||||
width: 15px;
|
|
||||||
height: 15px;
|
|
||||||
background: var(--switchOffFg);
|
|
||||||
border-radius: 999px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
|
Loading…
Reference in New Issue