abstraction, made popover optional, initial markup for better mute options

This commit is contained in:
Henry Jameson 2025-01-14 01:42:36 +02:00
parent 5a085d8e36
commit b831f34c06
9 changed files with 248 additions and 265 deletions

View file

@ -0,0 +1,83 @@
import StatusBookmarkFolderMenu from 'src/components/status_bookmark_folder_menu/status_bookmark_folder_menu.vue'
import Popover from 'src/components/popover/popover.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faPlus,
faMinus,
faCheck,
faTimes,
faWrench,
faReply,
faRetweet,
faStar,
faSmileBeam,
faEllipsisH,
faBookmark,
faEyeSlash,
faThumbtack,
faShareAlt,
faExternalLinkAlt,
faHistory
} from '@fortawesome/free-solid-svg-icons'
import {
faStar as faStarRegular
} from '@fortawesome/free-regular-svg-icons'
library.add(
faPlus,
faMinus,
faCheck,
faTimes,
faWrench,
faReply,
faRetweet,
faStar,
faStarRegular,
faSmileBeam,
faEllipsisH,
faBookmark,
faEyeSlash,
faThumbtack,
faShareAlt,
faExternalLinkAlt,
faHistory
)
export default {
props: [
'button',
'extra',
'status',
'funcArg',
'animationState',
'getClass',
'getComponent',
'doAction',
'close'
],
components: {
StatusBookmarkFolderMenu,
Popover
},
computed: {
buttonClass () {
if (!this.extra) console.log(this.button.name)
return [
this.button.name + '-button',
{
'main-button': this.extra,
'button-unstyled': !this.extra,
'-extra': this.extra,
'-quick': !this.extra,
'-active': this.button.active?.(this.funcArg),
disabled: this.button.interactive ? !this.button.interactive(this.funcArg) : false
}
]
}
}
}

View file

@ -2,6 +2,12 @@
/* stylelint-disable declaration-no-important */ /* stylelint-disable declaration-no-important */
.action-button { .action-button {
display: grid;
&.-with-extra {
grid-template-columns: 1fr calc(var(--__line-height) + 2 * var(--__horizontal-gap));
}
&.-quick { &.-quick {
display: grid; display: grid;
grid-template-columns: max-content auto; grid-template-columns: max-content auto;

View file

@ -1,8 +1,11 @@
<template> <template>
<div> <div
class="action-button"
:class="{ '-with-extra': button.name === 'bookmark' }"
>
<component <component
:is="getComponent(button)" :is="getComponent(button)"
class="main-button action-button" class="action-button-inner"
:class="buttonClass" :class="buttonClass"
role="menuitem" role="menuitem"
:tabindex="0" :tabindex="0"
@ -38,90 +41,48 @@
:icon="button.closeIndicator?.(funcArg) || 'minus'" :icon="button.closeIndicator?.(funcArg) || 'minus'"
/> />
</template> </template>
</FALayers><span>{{ $t(button.label(funcArg)) }}</span> </FALayers>
<span
v-if="extra"
class="action-label"
>
{{ $t(button.label(funcArg)) }}
</span>
<span
v-if="!extra && button.counter?.(funcArg) > 0"
class="action-counter"
>
{{ button.counter?.(funcArg) }}
</span>
<FAIcon <FAIcon
v-if="button.name === 'mute'" v-if="button.dropdown?.()"
class="chevron-icon" class="chevron-icon"
size="lg" size="lg"
icon="chevron-right" icon="chevron-right"
fixed-width fixed-width
/> />
</component> </component>
<Popover
trigger="hover"
placement="right"
:trigger-attrs="{ class: 'extra-button' }"
v-if="button.name === 'bookmark'"
>
<template #trigger>
<FAIcon
class="chevron-icon"
size="lg"
icon="chevron-right"
fixed-width
/>
</template>
<template #content>
<StatusBookmarkFolderMenu v-if="button.name === 'bookmark'" :status="$attrs.status" />
</template>
</Popover>
</div> </div>
</template> </template>
<script> <script src="./action_button.js"/>
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faPlus,
faMinus,
faCheck,
faTimes,
faWrench,
faReply,
faRetweet,
faStar,
faSmileBeam,
faEllipsisH,
faBookmark,
faEyeSlash,
faThumbtack,
faShareAlt,
faExternalLinkAlt,
faHistory
} from '@fortawesome/free-solid-svg-icons'
import {
faStar as faStarRegular
} from '@fortawesome/free-regular-svg-icons'
library.add(
faPlus,
faMinus,
faCheck,
faTimes,
faWrench,
faReply,
faRetweet,
faStar,
faStarRegular,
faSmileBeam,
faEllipsisH,
faBookmark,
faEyeSlash,
faThumbtack,
faShareAlt,
faExternalLinkAlt,
faHistory
)
export default {
props: [
'button',
'extra',
'status',
'funcArg',
'animationState',
'getClass',
'getComponent',
'doAction',
'close'
],
computed: {
buttonClass () {
return {
[this.button.name + '-button']: true,
'-extra': this.extra,
'-quick': !this.extra,
'-active': this.button.active?.(this.funcArg),
disabled: this.button.interactive ? !this.button.interactive(this.funcArg) : false
}
}
}
}
</script>
<style lang="scss" src="./action_button.scss"/> <style lang="scss" src="./action_button.scss"/>

View file

@ -0,0 +1,23 @@
import ActionButton from './action_button.vue'
import Popover from 'src/components/popover/popover.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faUser,
faGlobe,
faFolderTree
} from '@fortawesome/free-solid-svg-icons'
library.add(
faUser,
faGlobe,
faFolderTree
)
export default {
components: {
ActionButton,
Popover
},
props: ['button']
}

View file

@ -0,0 +1,58 @@
<template>
<Popover
trigger="hover"
placement="right"
v-if="button.dropdown?.()"
>
<template #trigger>
{{ props }}
<ActionButton
:button="button"
v-bind="$attrs"
/>
</template>
<template #content>
<div
v-if="button.name === 'mute'"
class="dropdown-menu"
:id="`popup-menu-${randomSeed}`"
role="menu"
>
<div class="menu-item dropdown-item extra-action -icon">
<button
class="main-button"
@click="() => {}"
>
<FAIcon icon="user" fixed-width />
{{ $t('status.mute_user') }}
</button>
</div>
<div class="menu-item dropdown-item extra-action -icon">
<button
class="main-button"
@click="() => {}"
>
<FAIcon icon="folder-tree" fixed-width />
{{ $t('status.mute_conversation') }}
</button>
</div>
<div class="menu-item dropdown-item extra-action -icon">
<button
class="main-button"
@click="() => {}"
>
<FAIcon icon="globe" fixed-width />
{{ $t('status.mute_domain') }}
</button>
</div>
</div>
</template>
</Popover>
<ActionButton
v-else
:button="button"
v-bind="$attrs"
/>
</template>
<script src="./action_button_container.js"/>

View file

@ -1,8 +1,7 @@
import { mapState } from 'vuex' import { mapState } from 'vuex'
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue' import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
import ActionButton from './action_button.vue' import ActionButtonContainer from './action_button_container.vue'
import StatusBookmarkFolderMenu from 'src/components/status_bookmark_folder_menu/status_bookmark_folder_menu.vue'
import Popover from 'src/components/popover/popover.vue' import Popover from 'src/components/popover/popover.vue'
import genRandomSeed from 'src/services/random_seed/random_seed.service.js' import genRandomSeed from 'src/services/random_seed/random_seed.service.js'
@ -136,22 +135,21 @@ const BUTTONS = [{
popover: 'emoji-picker' popover: 'emoji-picker'
}, { }, {
// ========= // =========
// MUTE CONVERSATION, my beloved // MUTE
// ========= // =========
name: 'mute', name: 'mute',
icon: 'eye-slash', icon: 'eye-slash',
label: ({ status }) => status.thread_muted label: 'status.mute_ellipsis',
? 'status.unmute_conversation'
: 'status.mute_conversation',
if: ({ loggedIn }) => loggedIn, if: ({ loggedIn }) => loggedIn,
toggleable: true, toggleable: true,
action ({ status, dispatch, emit }) { dropdown: true
if (status.thread_muted) { // action ({ status, dispatch, emit }) {
return dispatch('unmuteConversation', { id: status.id }) // if (status.thread_muted) {
} else { // return dispatch('unmuteConversation', { id: status.id })
return dispatch('muteConversation', { id: status.id }) // } else {
} // return dispatch('muteConversation', { id: status.id })
} // }
// }
}, { }, {
// ========= // =========
// PIN STATUS // PIN STATUS
@ -287,6 +285,7 @@ const StatusActionButtons = {
emits: ['toggleReplying'], emits: ['toggleReplying'],
data () { data () {
return { return {
Popover,
animationState: { animationState: {
retweet: false, retweet: false,
favorite: false favorite: false
@ -303,8 +302,7 @@ const StatusActionButtons = {
components: { components: {
Popover, Popover,
ConfirmModal, ConfirmModal,
ActionButton, ActionButtonContainer
StatusBookmarkFolderMenu
}, },
computed: { computed: {
...mapState({ ...mapState({

View file

@ -8,84 +8,12 @@
grid-auto-columns: 1fr; grid-auto-columns: 1fr;
grid-gap: 1em; grid-gap: 1em;
margin-top: var(--status-margin); margin-top: var(--status-margin);
.quick-action {
display: grid;
grid-template-columns: 1fr auto;
&.-pin {
margin: calc(-2px - 0.25em);
padding: 0.25em;
border: 2px dashed var(--icon);
border-radius: var(--roundness);
}
&.-pin,
&.-dropdown {
grid-template-columns: 1fr max-content;
}
.reply-button:not(.disabled) {
&:hover,
&.-active {
.svg-inline--fa {
color: var(--cBlue);
}
}
}
.retweet-button:not(.disabled) {
&:hover,
&.-active {
.svg-inline--fa {
color: var(--cGreen);
}
}
}
.favorite-button:not(.disabled) {
&:hover,
&.-active {
.svg-inline--fa {
color: var(--cOrange);
}
}
}
> button,
> a {
padding: 0.5em;
margin: -0.5em;
@include unfocused-style {
.focus-marker {
visibility: hidden;
}
.active-marker {
visibility: visible;
}
}
@include focused-style {
.focus-marker {
visibility: visible;
}
.active-marker {
visibility: hidden;
}
}
}
}
} }
} }
// popover // popover
/* stylelint-disable no-descending-specificity */ /* stylelint-disable no-descending-specificity */
.extra-action-buttons { .extra-action-buttons {
.extra-action { .extra-action {
display: grid;
grid-template-columns: 1fr;
grid-auto-flow: column; grid-auto-flow: column;
grid-auto-columns: auto; grid-auto-columns: auto;
grid-gap: 1em; grid-gap: 1em;

View file

@ -7,50 +7,17 @@
v-for="button in quickButtons" v-for="button in quickButtons"
:key="button.name" :key="button.name"
> >
<component <ActionButtonContainer
:is="getComponent(button)" :button="button"
class="button-unstyled action-button" :status="status"
:class="getClass(button)" :extra="false"
:disabled="getClass(button).disabled" :funcArg="funcArg"
role="button" :get-class="getClass"
:tabindex="0" :get-component="getComponent"
:title="$t(button.label(funcArg))" :animation-state="animationState"
@click.stop="getComponent(button) === 'button' && doAction(button)" :close="close"
:href="getComponent(button) == 'a' ? button.link?.(funcArg) || getRemoteInteractionLink : undefined" :do-action="doAction"
> />
<FALayers>
<FAIcon
class="fa-scale-110"
:icon="button.icon(funcArg)"
/>
<template v-if="!getClass(button).disabled && button.toggleable?.(funcArg) && button.active">
<FAIcon
v-if="button.active(funcArg)"
class="active-marker"
transform="shrink-6 up-9 right-12"
:icon="button.activeIndicator?.(funcArg) || 'check'"
/>
<FAIcon
v-if="!button.active(funcArg)"
class="focus-marker"
transform="shrink-6 up-9 right-12"
:icon="button.openIndicator?.(funcArg) || 'plus'"
/>
<FAIcon
v-else
class="focus-marker"
transform="shrink-6 up-9 right-12"
:icon="button.closeIndicator?.(funcArg) || 'minus'"
/>
</template>
</FALayers>
<span
class="action-counter"
v-if="button.counter?.(funcArg) > 0"
>
{{ button.counter?.(funcArg) }}
</span>
</component>
<button <button
v-if="showPin && currentUser" v-if="showPin && currentUser"
type="button" type="button"
@ -111,63 +78,17 @@
:disabled="getClass(button).disabled" :disabled="getClass(button).disabled"
:class="{ disabled: getClass(button).disabled }" :class="{ disabled: getClass(button).disabled }"
> >
<Popover <ActionButtonContainer
v-if="getComponent(button) === 'button'" :button="button"
trigger="hover" :status="status"
placement="right" :extra="true"
> :funcArg="funcArg"
<template #trigger> :get-class="getClass"
<ActionButton :get-component="getComponent"
:button="button" :animation-state="animationState"
:status="status" :close="close"
:extra="true" :do-action="doAction"
:funcArg="funcArg" />
:get-class="getClass"
:get-component="getComponent"
:animation-state="animationState"
:close="close"
:do-action="doAction"
/>
</template>
<template #content>
<template v-if="button.name === 'mute'">
<div
v-for="folder in folders"
:key="folder.id"
class="menu-item dropdown-item -icon"
>
<button
class="main-button"
@click="toggleFolder(folder.id)"
>
<span
class="input menu-checkbox -radio"
:class="{ 'menu-checkbox-checked': status.bookmark_folder_id == folder.id }"
/>
{{ folder.name }}
</button>
</div>
</template>
</template>
</Popover>
<Popover
trigger="hover"
placement="right"
:trigger-attrs="{ class: 'extra-button' }"
v-if="button.name === 'bookmark'"
>
<template #trigger>
<FAIcon
class="chevron-icon"
size="lg"
icon="chevron-right"
fixed-width
/>
</template>
<template #content>
<StatusBookmarkFolderMenu v-if="button.name === 'bookmark'" :status="funcArg.status" />
</template>
</Popover>
<button <button
v-if="showPin && currentUser" v-if="showPin && currentUser"
type="button" type="button"

View file

@ -1238,6 +1238,11 @@
"mentions": "Mentions", "mentions": "Mentions",
"replies_list": "Replies:", "replies_list": "Replies:",
"replies_list_with_others": "Replies (+{numReplies} other): | Replies (+{numReplies} others):", "replies_list_with_others": "Replies (+{numReplies} other): | Replies (+{numReplies} others):",
"mute_ellipsis": "Mute…",
"mute_user": "Mute user",
"unmute_user": "Unmute user",
"mute_domain": "Mute domain",
"unmute_domain": "Unmute domain",
"mute_conversation": "Mute conversation", "mute_conversation": "Mute conversation",
"unmute_conversation": "Unmute conversation", "unmute_conversation": "Unmute conversation",
"status_unavailable": "Status unavailable", "status_unavailable": "Status unavailable",