Skip to content

Commit

Permalink
enhance(frontend): ユーザーメニューでスイッチでユーザーリストに追加・削除できるように (#11439)
Browse files Browse the repository at this point in the history
* メニューのトグルをいい感じにする

* user list toggle!

* add changelog

* fix

* stop
  • Loading branch information
tamaina authored Aug 1, 2023
1 parent 2b4c8c9 commit 8a72a05
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 85 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
- OAuth 2.0のサポート

### Client
- メニューのスイッチの動作を改善
- Enhance: ユーザーメニューでスイッチでユーザーリストに追加・削除できるように
- Enhance: 自分が押したリアクションのデザインを改善
- Fix: サーバー情報画面(`/instance-info/{domain}`)でブロックができないのを修正
- Fix: 未読のお知らせの「わかった」をクリック・タップしてもその場で「わかった」が消えない問題を修正
Expand Down
57 changes: 47 additions & 10 deletions packages/frontend/src/components/MkMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/>
<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
</button>
<span v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" :class="$style.item" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
<MkSwitch v-model="item.ref" :disabled="item.disabled" class="form-switch">{{ item.text }}</MkSwitch>
</span>
<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)" />
<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)">
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
<span>{{ item.text }}</span>
Expand All @@ -63,8 +64,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { focusPrev, focusNext } from '@/scripts/focus';
import MkSwitch from '@/components/MkSwitch.vue';
import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu';
import MkSwitchButton from '@/components/MkSwitch.button.vue';
import { MenuItem, InnerMenuItem, OuterMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu';
import * as os from '@/os';
import { i18n } from '@/i18n';
Expand Down Expand Up @@ -145,17 +146,17 @@ function onItemMouseLeave(item) {
if (childCloseTimer) window.clearTimeout(childCloseTimer);
}
let childrenCache = new WeakMap();
async function showChildren(item: MenuItem, ev: MouseEvent) {
const children = ref([]);
let childrenCache = new WeakMap<MenuParent, OuterMenuItem[]>();
async function showChildren(item: MenuParent, ev: MouseEvent) {
const children = ref<OuterMenuItem[]>([]);
if (childrenCache.has(item)) {
children.value = childrenCache.get(item);
children.value = childrenCache.get(item)!;
} else {
if (typeof item.children === 'function') {
children.value = [{
type: 'pending',
}];
item.children().then(x => {
Promise.resolve(item.children()).then(x => {
children.value = x;
childrenCache.set(item, x);
});
Expand Down Expand Up @@ -191,6 +192,11 @@ function focusDown() {
focusNext(document.activeElement);
}
function switchItem(item: MenuSwitch & { ref: any }) {
if (item.disabled) return;
item.ref = !item.ref;
}
onMounted(() => {
if (props.viaKeyboard) {
nextTick(() => {
Expand Down Expand Up @@ -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 {
margin-right: 8px;
}
Expand Down
8 changes: 5 additions & 3 deletions packages/frontend/src/components/MkNote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -408,14 +408,16 @@ function onContextmenu(ev: MouseEvent): void {
ev.preventDefault();
react();
} else {
os.contextMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }), ev).then(focus);
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
os.contextMenu(menu, ev).then(focus).finally(cleanup);
}
}
function menu(viaKeyboard = false): void {
os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }), menuButton.value, {
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
os.popupMenu(menu, menuButton.value, {
viaKeyboard,
}).then(focus);
}).then(focus).finally(cleanup);
}
async function clip() {
Expand Down
8 changes: 5 additions & 3 deletions packages/frontend/src/components/MkNoteDetailed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -385,14 +385,16 @@ function onContextmenu(ev: MouseEvent): void {
ev.preventDefault();
react();
} else {
os.contextMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted }), ev).then(focus);
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted });
os.contextMenu(menu, ev).then(focus).finally(cleanup);
}
}
function menu(viaKeyboard = false): void {
os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted }), menuButton.value, {
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted });
os.popupMenu(menu, menuButton.value, {
viaKeyboard,
}).then(focus);
}).then(focus).finally(cleanup);
}
async function clip() {
Expand Down
88 changes: 88 additions & 0 deletions packages/frontend/src/components/MkSwitch.button.vue
Original file line number Diff line number Diff line change
@@ -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.stop="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>
50 changes: 4 additions & 46 deletions packages/frontend/src/components/MkSwitch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="$style.input"
@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">
<div :class="$style.knob"></div>
</span>
<XButton :checked="checked" :disabled="disabled" @toggle="toggle" />
<span :class="$style.body">
<!-- TODO: 無名slotの方は廃止 -->
<span :class="$style.label" @click="toggle"><slot name="label"></slot><slot></slot></span>
Expand All @@ -25,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only

<script lang="ts" setup>
import { toRefs, Ref } from 'vue';
import { i18n } from '@/i18n';
import XButton from '@/components/MkSwitch.button.vue';
const props = defineProps<{
modelValue: boolean | Ref<boolean>;
Expand All @@ -36,7 +34,6 @@ const emit = defineEmits<{
(ev: 'update:modelValue', v: boolean): void;
}>();
let button = $shallowRef<HTMLElement>();
const checked = toRefs(props).modelValue;
const toggle = () => {
if (props.disabled) return;
Expand Down Expand Up @@ -66,17 +63,8 @@ const toggle = () => {
cursor: not-allowed;
}
&.checked {
> .button {
background-color: var(--switchOnBg) !important;
border-color: var(--switchOnBg) !important;
> .knob {
left: 12px;
background: var(--switchOnFg);
}
}
}
//&.checked {
//}
}
.input {
Expand All @@ -86,36 +74,6 @@ const toggle = () => {
opacity: 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 {
margin-left: 12px;
margin-top: 2px;
Expand Down
3 changes: 2 additions & 1 deletion packages/frontend/src/components/MkUserPopup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ let top = $ref(0);
let left = $ref(0);
function showMenu(ev: MouseEvent) {
os.popupMenu(getUserMenu(user), ev.currentTarget ?? ev.target);
const { menu, cleanup } = getUserMenu(user);
os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup);
}
onMounted(() => {
Expand Down
3 changes: 2 additions & 1 deletion packages/frontend/src/pages/user/home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ const age = $computed(() => {
});
function menu(ev) {
os.popupMenu(getUserMenu(props.user, router), ev.currentTarget ?? ev.target);
const { menu, cleanup } = getUserMenu(props.user, router);
os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup);
}
function parallaxLoop() {
Expand Down
Loading

0 comments on commit 8a72a05

Please sign in to comment.