fix: layout fixes for RTL languages (#591)
* fix: rtl arrows on settings page * fix: border on settings page for RTL languages * fix: RTL fixes for logo, search box and logout icon * fix: RTL layout bugs in conversations * chore: remove rtl setting icon * improve arabic locale * add new entries to arabic locale * chore: include number format * fix: RTL layout on several pages * fix: RTL layout of account header and sign in modal * fix: always display account handle in LTR * fix: move character counter in publish widget to left side for RTL * fix: remove border-ss-none unocss rule * fix: many RTL fixes * fix: RTL fixes for many pages * fix: use viewer's direction in all content * chore: use new arabic plural rules * chore: flip arrow on main content header * chore: fix StatusPoll and show_new_items for zh-TW * chore: StatusPoll tooltip on bottom * chore: add `en` variants to i18n conf * chore: update entry to use new plural rule * fix: automatic content direction for status * fix: direction for account handle * fix: direction of polls Co-authored-by: userquin <userquin@gmail.com> Co-authored-by: Jean-Paul Khawam <jeanpaulkhawam@protonmail.com> Co-authored-by: Daniel Roe <daniel@roe.dev>
This commit is contained in:
parent
c5304be775
commit
727d05915f
50 changed files with 347 additions and 222 deletions
|
@ -94,7 +94,7 @@ async function editStatus() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<CommonDropdown flex-none ml3 placement="bottom" :eager-mount="command">
|
||||
<CommonDropdown flex-none ms3 placement="bottom" :eager-mount="command">
|
||||
<StatusActionButton
|
||||
:content="$t('action.more')"
|
||||
color="text-purple"
|
||||
|
|
|
@ -97,14 +97,13 @@ const isDM = $computed(() => status.visibility === 'direct')
|
|||
tabindex="0"
|
||||
focus:outline-none focus-visible:ring="2 primary"
|
||||
:lang="status.language ?? undefined"
|
||||
:dir="status.language ? 'auto' : 'ltr'"
|
||||
@click="onclick"
|
||||
@keydown.enter="onclick"
|
||||
>
|
||||
<div flex justify-between>
|
||||
<slot name="meta">
|
||||
<div v-if="rebloggedBy && !collapseRebloggedBy" text-secondary text-sm ws-nowrap flex="~" gap-1 items-center py1 bg-base>
|
||||
<div i-ri:repeat-fill mr-1 text-primary />
|
||||
<div i-ri:repeat-fill me-1 text-primary />
|
||||
<AccountInlineInfo font-bold :account="rebloggedBy" :avatar="!avatarOnAvatar" />
|
||||
</div>
|
||||
<div v-else />
|
||||
|
@ -113,11 +112,11 @@ const isDM = $computed(() => status.visibility === 'direct')
|
|||
</div>
|
||||
<div flex gap-3 :class="{ 'text-secondary': faded }">
|
||||
<div relative>
|
||||
<div v-if="showRebloggedByAvatarOnAvatar" absolute top--3px left--0.8 rtl-left-none rtl-right--0.8 z--1 w-25px h-25px rounded-full>
|
||||
<div v-if="showRebloggedByAvatarOnAvatar" absolute top--3px inset-is--0.8 z--1 w-25px h-25px rounded-full>
|
||||
<AccountAvatar :account="rebloggedBy" />
|
||||
</div>
|
||||
<div v-else-if="collapseRebloggedBy" absolute left--0.8 rtl-left-none rtl-right--0.8 w-5.5 h-5.5 rounded-full bg-base>
|
||||
<div i-ri:repeat-fill mr-1 text-primary text-sm />
|
||||
<div v-else-if="collapseRebloggedBy" absolute inset-is--0.8 w-5.5 h-5.5 rounded-full bg-base>
|
||||
<div i-ri:repeat-fill me-1 text-primary text-sm />
|
||||
</div>
|
||||
<AccountHoverWrapper :account="status.account">
|
||||
<NuxtLink :to="getAccountRoute(status.account)" rounded-full>
|
||||
|
@ -133,12 +132,12 @@ const isDM = $computed(() => status.visibility === 'direct')
|
|||
<AccountHoverWrapper :account="status.account">
|
||||
<StatusAccountDetails :account="status.account" />
|
||||
</AccountHoverWrapper>
|
||||
<div v-if="!directReply && collapseReplyingTo" flex="~" pl-1 items-center justify-center>
|
||||
<div v-if="!directReply && collapseReplyingTo" flex="~" ps-1 items-center justify-center>
|
||||
<StatusReplyingTo :collapsed="true" :status="status" :class="faded ? 'text-secondary-light' : ''" />
|
||||
</div>
|
||||
<div flex-auto />
|
||||
<div v-if="!isZenMode" text-sm text-secondary flex="~ row nowrap" hover:underline>
|
||||
<AccountBotIndicator v-if="status.account.bot" mr-2 />
|
||||
<AccountBotIndicator v-if="status.account.bot" me-2 />
|
||||
<div flex>
|
||||
<CommonTooltip :content="createdAt">
|
||||
<a :title="status.createdAt" :href="getStatusRoute(status).href" @click.prevent="go($event)">
|
||||
|
@ -150,7 +149,7 @@ const isDM = $computed(() => status.visibility === 'direct')
|
|||
<StatusEditIndicator :status="status" inline />
|
||||
</div>
|
||||
</div>
|
||||
<StatusActionsMore v-if="actions !== false" :status="status" mr--2 />
|
||||
<StatusActionsMore v-if="actions !== false" :status="status" me--2 />
|
||||
</div>
|
||||
<StatusContent :status="status" :context="context" mb2 :class="{ mt2: isDM }" />
|
||||
<div>
|
||||
|
|
|
@ -23,7 +23,7 @@ const isFiltered = $computed(() => filterPhrase && (context && context !== 'deta
|
|||
<div
|
||||
space-y-3
|
||||
:class="{
|
||||
'pt2 pb0.5 px3.5 br2 border-1 rounded-3 rounded-tl-none': isDM,
|
||||
'pt2 pb0.5 px3.5 border-1 rounded-3 rounded-bs-is-none': isDM,
|
||||
'bg-fade border-primary-light': isDM,
|
||||
}"
|
||||
>
|
||||
|
|
|
@ -29,9 +29,9 @@ const isDM = $computed(() => status.visibility === 'direct')
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :id="`status-${status.id}`" flex flex-col gap-2 pt2 pb1 px-4 relative :lang="status.language ?? undefined" dir="auto">
|
||||
<StatusActionsMore :status="status" absolute right-2 top-2 />
|
||||
<NuxtLink :to="getAccountRoute(status.account)" rounded-full hover:bg-active transition-100 pr5 mr-a>
|
||||
<div :id="`status-${status.id}`" flex flex-col gap-2 pt2 pb1 px-4 relative :lang="status.language ?? undefined">
|
||||
<StatusActionsMore :status="status" absolute inset-ie-2 top-2 />
|
||||
<NuxtLink :to="getAccountRoute(status.account)" rounded-full hover:bg-active transition-100 pe5 me-a>
|
||||
<AccountHoverWrapper :account="status.account">
|
||||
<AccountInfo :account="status.account" />
|
||||
</AccountHoverWrapper>
|
||||
|
@ -44,7 +44,7 @@ const isDM = $computed(() => status.visibility === 'direct')
|
|||
:status="status"
|
||||
:inline="false"
|
||||
>
|
||||
<span ml1 font-bold cursor-pointer>{{ $t('state.edited') }}</span>
|
||||
<span ms1 font-bold cursor-pointer>{{ $t('state.edited') }}</span>
|
||||
</StatusEditIndicator>
|
||||
</div>
|
||||
<div>·</div>
|
||||
|
|
|
@ -13,7 +13,7 @@ function toPercentage(num: number) {
|
|||
const timeAgoOptions = useTimeAgoOptions()
|
||||
const expiredTimeAgo = useTimeAgo(poll.expiresAt!, timeAgoOptions)
|
||||
const expiredTimeFormatted = useFormattedDateTime(poll.expiresAt!)
|
||||
const { formatHumanReadableNumber } = useHumanReadableNumber()
|
||||
const { formatHumanReadableNumber, formatNumber, formatPercentage, forSR } = useHumanReadableNumber()
|
||||
|
||||
const masto = useMasto()
|
||||
async function vote(e: Event) {
|
||||
|
@ -32,10 +32,15 @@ async function vote(e: Event) {
|
|||
|
||||
await masto.poll.vote(poll.id, { choices })
|
||||
}
|
||||
|
||||
const votersCount = $computed(() => poll.votersCount ?? 0)
|
||||
const votersCountHR = $computed(() => formatHumanReadableNumber(votersCount))
|
||||
const votersCountNumber = $computed(() => formatNumber(votersCount))
|
||||
const votersCountSR = $computed(() => forSR(votersCount))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div flex flex-col w-full items-stretch gap-3>
|
||||
<div flex flex-col w-full items-stretch gap-3 dir="auto">
|
||||
<form v-if="!poll.voted && !poll.expired" flex flex-col gap-4 accent-primary @click.stop="noop" @submit.prevent="vote">
|
||||
<label v-for="(option, index) of poll.options" :key="index" flex items-center gap-2 px-2>
|
||||
<input name="choices" :value="index" :type="poll.multiple ? 'checkbox' : 'radio'">
|
||||
|
@ -50,17 +55,23 @@ async function vote(e: Event) {
|
|||
<div flex justify-between pb-2 w-full>
|
||||
<span inline-flex align-items>
|
||||
{{ option.title }}
|
||||
<span v-if="poll.voted && poll.ownVotes?.includes(index)" ml-2 mt-1 inline-block i-ri:checkbox-circle-line />
|
||||
<span v-if="poll.voted && poll.ownVotes?.includes(index)" ms-2 mt-1 inline-block i-ri:checkbox-circle-line />
|
||||
</span>
|
||||
<span text-primary-active> {{ poll.votesCount ? toPercentage((option.votesCount || 0) / (poll.votesCount)) : '0%' }}</span>
|
||||
<span text-primary-active> {{ formatPercentage(votersCount > 0 ? (option.votesCount || 0) / votersCount : 0) }}</span>
|
||||
</div>
|
||||
<div class="bg-gray/40" rounded-l-sm rounded-r-lg h-5px w-full>
|
||||
<div bg-primary-active h-full class="w-[var(--bar-width)]" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div text-sm>
|
||||
{{ $t('status.poll.count', [formatHumanReadableNumber(poll.votersCount ?? 0)]) }}
|
||||
<div text-sm flex="~ inline" gap-x-1>
|
||||
<i18n-t keypath="status.poll.count" :plural="votersCount">
|
||||
<CommonTooltip v-if="votersCountSR" :content="votersCountNumber" placement="bottom">
|
||||
<span aria-hidden="true">{{ votersCountHR }}</span>
|
||||
<span sr-only>{{ votersCountNumber }}</span>
|
||||
</CommonTooltip>
|
||||
<span v-else>{{ votersCountNumber }}</span>
|
||||
</i18n-t>
|
||||
·
|
||||
<CommonTooltip :content="expiredTimeFormatted" class="inline-block" placement="right">
|
||||
<time :datetime="poll.expiresAt!">{{ $t(poll.expired ? 'status.poll.finished' : 'status.poll.ends', [expiredTimeAgo]) }}</time>
|
||||
|
|
|
@ -102,10 +102,10 @@ const meta = $computed(() => {
|
|||
<span v-else>{{ meta.user }}</span>
|
||||
</a>
|
||||
<a sm:text-lg :href="card.url" target="_blank">
|
||||
<span v-if="meta.type === 'issue'" text-secondary-light mr-2>
|
||||
<span v-if="meta.type === 'issue'" text-secondary-light me-2>
|
||||
#{{ meta.number }}
|
||||
</span>
|
||||
<span v-if="meta.type === 'pull'" text-secondary-light mr-2>
|
||||
<span v-if="meta.type === 'pull'" text-secondary-light me-2>
|
||||
PR #{{ meta.number }}
|
||||
</span>
|
||||
<span text-secondary leading-tight>{{ meta.details }}</span>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue