mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(app): draw the mention badge cutout from the badge itself (#1868)
This commit is contained in:
1 parent
677e6e5d6e
commit
5b8a46d087
7 files changed
+69
-73
No files matched your search
@@ -306,14 +306,9 @@
|
|||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.guildBadgeActive::before,
|
.guildBadgeActive,
|
||||||
.guildBadge:not(:empty)::before {
|
.guildBadge:not(:empty) {
|
||||||
content: '';
|
--mention-badge-cutout-surface: var(--guild-badge-surface);
|
||||||
position: absolute;
|
|
||||||
inset: -0.1875rem;
|
|
||||||
z-index: -1;
|
|
||||||
border-radius: calc(var(--radius-md) + 0.1875rem);
|
|
||||||
background-color: var(--guild-badge-surface);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.guildVoiceBadge {
|
.guildVoiceBadge {
|
||||||
|
|||||||
+3
-8
@@ -145,14 +145,9 @@
|
|||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.folderBadgeActive::before,
|
.folderBadgeActive,
|
||||||
.folderBadge:not(:empty)::before {
|
.folderBadge:not(:empty) {
|
||||||
content: '';
|
--mention-badge-cutout-surface: var(--guild-badge-surface);
|
||||||
position: absolute;
|
|
||||||
inset: -0.1875rem;
|
|
||||||
z-index: -1;
|
|
||||||
border-radius: calc(var(--radius-md) + 0.1875rem);
|
|
||||||
background-color: var(--guild-badge-surface);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.expandedFolderBackground {
|
.expandedFolderBackground {
|
||||||
|
|||||||
@@ -759,14 +759,14 @@ export const GuildFolderItem = observer((props: GuildFolderItemProps) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const MINI_GUILD_INITIALS_MAX_LENGTH = 2;
|
||||||
|
|
||||||
interface MiniGuildIconProps {
|
interface MiniGuildIconProps {
|
||||||
readonly guild: Guild;
|
readonly guild: Guild;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MiniGuildIcon = observer(({guild}: MiniGuildIconProps) => {
|
const MiniGuildIcon = observer(({guild}: MiniGuildIconProps) => {
|
||||||
const iconURL = AvatarSourceUtils.getGuildIconURL(guild, false);
|
const iconURL = AvatarSourceUtils.getGuildIconURL(guild, false);
|
||||||
const initials = getInitialsFromName(guild.name);
|
|
||||||
const displayInitials = truncateInitials(initials, 2);
|
|
||||||
if (iconURL !== '') {
|
if (iconURL !== '') {
|
||||||
return (
|
return (
|
||||||
<flx-app-mini-guild-icon
|
<flx-app-mini-guild-icon
|
||||||
@@ -785,7 +785,7 @@ const MiniGuildIcon = observer(({guild}: MiniGuildIconProps) => {
|
|||||||
className={styles.miniGuildInitials}
|
className={styles.miniGuildInitials}
|
||||||
data-flx="app.sidebar-nav.guild-folder-item.mini-guild-icon.mini-guild-initials"
|
data-flx="app.sidebar-nav.guild-folder-item.mini-guild-icon.mini-guild-initials"
|
||||||
>
|
>
|
||||||
{displayInitials}
|
{truncateInitials(getInitialsFromName(guild.name), MINI_GUILD_INITIALS_MAX_LENGTH)}
|
||||||
</span>
|
</span>
|
||||||
</flx-app-mini-guild-icon>
|
</flx-app-mini-guild-icon>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -59,7 +59,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.itemSlot {
|
.itemSlot {
|
||||||
--guild-rail-skeleton-badge-size: var(--mention-badge-small-size);
|
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -82,18 +81,6 @@
|
|||||||
min-height: var(--guild-list-item-box-size);
|
min-height: var(--guild-list-item-box-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mentionBadge {
|
|
||||||
position: absolute;
|
|
||||||
right: calc(50% - var(--guild-icon-size) / 2 - 0.25rem);
|
|
||||||
bottom: calc(50% - var(--guild-icon-size) / 2 - 0.25rem);
|
|
||||||
box-shadow: 0 0 0 0.1875rem var(--guild-badge-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
.folderSlot .mentionBadge {
|
|
||||||
right: -0.25rem;
|
|
||||||
bottom: -0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.itemsTrailingGapCancel {
|
.itemsTrailingGapCancel {
|
||||||
margin-bottom: calc(-1 * var(--guild-list-item-gap));
|
margin-bottom: calc(-1 * var(--guild-list-item-gap));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import {useState} from 'react';
|
|||||||
|
|
||||||
const GUILD_ICON_SIZE = skeletonSurfaceVar('--guild-icon-size');
|
const GUILD_ICON_SIZE = skeletonSurfaceVar('--guild-icon-size');
|
||||||
const FOLDER_ICON_SIZE = skeletonSurfaceVar('--guild-list-item-box-size');
|
const FOLDER_ICON_SIZE = skeletonSurfaceVar('--guild-list-item-box-size');
|
||||||
const MENTION_BADGE_SIZE = 'var(--guild-rail-skeleton-badge-size)';
|
|
||||||
const GUILD_PLACEHOLDER_COUNT = 6;
|
const GUILD_PLACEHOLDER_COUNT = 6;
|
||||||
const SHOWS_DOWNLOAD_ACTION = !Platform.isElectron && !Platform.isPWA;
|
const SHOWS_DOWNLOAD_ACTION = !Platform.isElectron && !Platform.isPWA;
|
||||||
const FALLBACK_ORGANIZED_ITEMS: ReadonlyArray<RememberedSkeletonGuildRailItem> = Object.freeze(
|
const FALLBACK_ORGANIZED_ITEMS: ReadonlyArray<RememberedSkeletonGuildRailItem> = Object.freeze(
|
||||||
@@ -190,17 +189,6 @@ function showsSkeletonIndicator({indicator, selected}: SkeletonIndicatorVisibili
|
|||||||
return indicator !== SkeletonGuildRailItemIndicator.NONE;
|
return indicator !== SkeletonGuildRailItemIndicator.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MentionBadgePlaceholder = () => (
|
|
||||||
<SkeletonBlock
|
|
||||||
width={MENTION_BADGE_SIZE}
|
|
||||||
height={MENTION_BADGE_SIZE}
|
|
||||||
radius={SkeletonRadius.MEDIUM}
|
|
||||||
emphasis={SkeletonEmphasis.MUTED}
|
|
||||||
className={styles.mentionBadge}
|
|
||||||
data-flx="app.skeleton.guild-rail-skeleton.mention-badge-placeholder.mention-badge"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const GuildPlaceholder = ({
|
const GuildPlaceholder = ({
|
||||||
indicator,
|
indicator,
|
||||||
selected,
|
selected,
|
||||||
@@ -223,9 +211,6 @@ const GuildPlaceholder = ({
|
|||||||
size={GUILD_ICON_SIZE}
|
size={GUILD_ICON_SIZE}
|
||||||
data-flx="app.skeleton.guild-rail-skeleton.guild-placeholder.skeleton-circle"
|
data-flx="app.skeleton.guild-rail-skeleton.guild-placeholder.skeleton-circle"
|
||||||
/>
|
/>
|
||||||
{indicator === SkeletonGuildRailItemIndicator.MENTION && (
|
|
||||||
<MentionBadgePlaceholder data-flx="app.skeleton.guild-rail-skeleton.guild-placeholder.mention-badge-placeholder" />
|
|
||||||
)}
|
|
||||||
</flx-app-guild-rail-skeleton-item-slot>
|
</flx-app-guild-rail-skeleton-item-slot>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -326,9 +311,6 @@ const CollapsedFolderPlaceholder = ({
|
|||||||
</flx-app-guild-rail-skeleton-collapsed-folder-grid>
|
</flx-app-guild-rail-skeleton-collapsed-folder-grid>
|
||||||
)}
|
)}
|
||||||
</flx-app-guild-rail-skeleton-collapsed-folder>
|
</flx-app-guild-rail-skeleton-collapsed-folder>
|
||||||
{indicator === SkeletonGuildRailItemIndicator.MENTION && (
|
|
||||||
<MentionBadgePlaceholder data-flx="app.skeleton.guild-rail-skeleton.collapsed-folder-placeholder.mention-badge-placeholder" />
|
|
||||||
)}
|
|
||||||
</flx-app-guild-rail-skeleton-item-slot>
|
</flx-app-guild-rail-skeleton-item-slot>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -354,9 +336,6 @@ const ExpandedFolderGuildPlaceholder = ({
|
|||||||
size={GUILD_ICON_SIZE}
|
size={GUILD_ICON_SIZE}
|
||||||
data-flx="app.skeleton.guild-rail-skeleton.expanded-folder-guild-placeholder.skeleton-circle"
|
data-flx="app.skeleton.guild-rail-skeleton.expanded-folder-guild-placeholder.skeleton-circle"
|
||||||
/>
|
/>
|
||||||
{indicator === SkeletonGuildRailItemIndicator.MENTION && (
|
|
||||||
<MentionBadgePlaceholder data-flx="app.skeleton.guild-rail-skeleton.expanded-folder-guild-placeholder.mention-badge-placeholder" />
|
|
||||||
)}
|
|
||||||
</flx-app-guild-rail-skeleton-expanded-folder-guild>
|
</flx-app-guild-rail-skeleton-expanded-folder-guild>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -32,5 +32,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.animatedWrapper {
|
.animatedWrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animatedWrapper::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: -0.1875rem;
|
||||||
|
z-index: -1;
|
||||||
|
border-radius: calc(var(--radius-md) + 0.1875rem);
|
||||||
|
background-color: var(--mention-badge-cutout-surface, transparent);
|
||||||
}
|
}
|
||||||
@@ -2,12 +2,49 @@
|
|||||||
|
|
||||||
import Accessibility from '@app/features/accessibility/state/Accessibility';
|
import Accessibility from '@app/features/accessibility/state/Accessibility';
|
||||||
import styles from '@app/features/ui/components/MentionBadge.module.css';
|
import styles from '@app/features/ui/components/MentionBadge.module.css';
|
||||||
|
import {
|
||||||
|
AnimePresence,
|
||||||
|
type AnimeTarget,
|
||||||
|
type AnimeTween,
|
||||||
|
AnimeTweenType,
|
||||||
|
createAnimeFlxElement,
|
||||||
|
} from '@app/features/ui/motion/AnimeElement';
|
||||||
import {getCurrentLocale} from '@app/features/user/utils/LocaleUtils';
|
import {getCurrentLocale} from '@app/features/user/utils/LocaleUtils';
|
||||||
import {formatCompactNumber, formatNumber} from '@pkgs/number_utils/src/NumberFormatting';
|
import {formatCompactNumber, formatNumber} from '@pkgs/number_utils/src/NumberFormatting';
|
||||||
import {clsx} from 'clsx';
|
import {clsx} from 'clsx';
|
||||||
import {AnimatePresence, motion} from 'framer-motion';
|
|
||||||
import {observer} from 'mobx-react-lite';
|
import {observer} from 'mobx-react-lite';
|
||||||
|
|
||||||
|
const MentionBadgeSurface = createAnimeFlxElement('flx-ui-mention-badge-surface');
|
||||||
|
|
||||||
|
const HIDDEN_MENTION_BADGE: AnimeTarget = Object.freeze({opacity: 0, scale: 0.85});
|
||||||
|
const VISIBLE_MENTION_BADGE: AnimeTarget = Object.freeze({opacity: 1, scale: 1});
|
||||||
|
|
||||||
|
interface MentionBadgeMotion {
|
||||||
|
readonly from: AnimeTarget;
|
||||||
|
readonly to: AnimeTarget;
|
||||||
|
readonly tween: AnimeTween;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MENTION_BADGE_LEAVE = false as const;
|
||||||
|
|
||||||
|
const STANDARD_MENTION_BADGE_MOTION: MentionBadgeMotion = Object.freeze({
|
||||||
|
from: HIDDEN_MENTION_BADGE,
|
||||||
|
to: VISIBLE_MENTION_BADGE,
|
||||||
|
tween: Object.freeze({type: AnimeTweenType.SPRING, stiffness: 500, damping: 22}),
|
||||||
|
});
|
||||||
|
const REDUCED_MENTION_BADGE_MOTION: MentionBadgeMotion = Object.freeze({
|
||||||
|
from: VISIBLE_MENTION_BADGE,
|
||||||
|
to: VISIBLE_MENTION_BADGE,
|
||||||
|
tween: Object.freeze({duration: 0}),
|
||||||
|
});
|
||||||
|
|
||||||
|
function resolveMentionBadgeMotion(useReducedMotion: boolean): MentionBadgeMotion {
|
||||||
|
if (useReducedMotion) {
|
||||||
|
return REDUCED_MENTION_BADGE_MOTION;
|
||||||
|
}
|
||||||
|
return STANDARD_MENTION_BADGE_MOTION;
|
||||||
|
}
|
||||||
|
|
||||||
const formatMentionCount = (mentionCount: number) => {
|
const formatMentionCount = (mentionCount: number) => {
|
||||||
const locale = getCurrentLocale();
|
const locale = getCurrentLocale();
|
||||||
if (mentionCount > 99 && mentionCount < 1000) {
|
if (mentionCount > 99 && mentionCount < 1000) {
|
||||||
@@ -38,34 +75,25 @@ export const MentionBadge = observer(({mentionCount, size = 'medium'}: MentionBa
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
export const MentionBadgeAnimated = observer(({mentionCount, size = 'medium'}: MentionBadgeProps) => {
|
export const MentionBadgeAnimated = observer(({mentionCount, size = 'medium'}: MentionBadgeProps) => {
|
||||||
const shouldAnimate = !Accessibility.useReducedMotion;
|
const motion = resolveMentionBadgeMotion(Accessibility.useReducedMotion);
|
||||||
if (!shouldAnimate) {
|
|
||||||
return mentionCount > 0 ? (
|
|
||||||
<MentionBadge
|
|
||||||
mentionCount={mentionCount}
|
|
||||||
size={size}
|
|
||||||
data-flx="ui.mention-badge.mention-badge-animated.mention-badge"
|
|
||||||
/>
|
|
||||||
) : null;
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence initial={false} mode="wait" data-flx="ui.mention-badge.mention-badge-animated.animate-presence">
|
<AnimePresence enterOnMount={false} data-flx="ui.mention-badge.mention-badge-animated.anime-presence">
|
||||||
{mentionCount > 0 && (
|
{mentionCount > 0 && (
|
||||||
<motion.div
|
<MentionBadgeSurface
|
||||||
className={styles.animatedWrapper}
|
className={styles.animatedWrapper}
|
||||||
initial={{opacity: 0, scale: 0.85}}
|
from={motion.from}
|
||||||
animate={{opacity: 1, scale: 1}}
|
to={motion.to}
|
||||||
exit={{opacity: 0, scale: 0.85}}
|
leave={MENTION_BADGE_LEAVE}
|
||||||
transition={{type: 'spring', stiffness: 500, damping: 22}}
|
tween={motion.tween}
|
||||||
data-flx="ui.mention-badge.mention-badge-animated.animated-wrapper"
|
data-flx="ui.mention-badge.mention-badge-animated.animated-wrapper"
|
||||||
>
|
>
|
||||||
<MentionBadge
|
<MentionBadge
|
||||||
mentionCount={mentionCount}
|
mentionCount={mentionCount}
|
||||||
size={size}
|
size={size}
|
||||||
data-flx="ui.mention-badge.mention-badge-animated.mention-badge--2"
|
data-flx="ui.mention-badge.mention-badge-animated.mention-badge"
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</MentionBadgeSurface>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimePresence>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user