pl-fe: add too many subtle animations

Signed-off-by: mkljczk <git@mkljczk.pl>
This commit is contained in:
mkljczk
2025-02-03 12:45:43 +01:00
parent 40464279b4
commit 86c0d32f4c
4 changed files with 118 additions and 103 deletions

View File

@ -182,7 +182,12 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
theme='muted'
size='xs'
onClick={toggleExpanded}
icon={expanded ? require('@tabler/icons/outline/chevron-up.svg') : require('@tabler/icons/outline/chevron-down.svg')}
icon={require('@tabler/icons/outline/chevron-down.svg')}
iconClassName={clsx(
'transition-transform', {
'rotate-180': expanded,
},
)}
>
{expanded
? <FormattedMessage id='status.spoiler.collapse' defaultMessage='Collapse' />
@ -193,112 +198,114 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
);
}
if (expandable && !expanded) return <>{output}</>;
const hasPoll = !!status.poll_id;
let quote;
if (!expandable || expanded) {
let quote;
if (withMedia && status.quote_id) {
if ((status.quote_visible ?? true) === false) {
quote = (
<div className='quoted-status-tombstone'>
<p><FormattedMessage id='statuses.quote_tombstone' defaultMessage='Post is unavailable.' /></p>
</div>
);
if (withMedia && status.quote_id) {
if ((status.quote_visible ?? true) === false) {
quote = (
<div className='quoted-status-tombstone'>
<p><FormattedMessage id='statuses.quote_tombstone' defaultMessage='Post is unavailable.' /></p>
</div>
);
} else {
quote = <QuotedStatus statusId={status.quote_id} />;
}
}
const media = withMedia && ((quote || status.card || status.media_attachments.length > 0)) && (
<Stack space={4} key='media'>
{(status.media_attachments.length > 0 || (status.card && !quote)) && (
<div className='relative'>
<SensitiveContentOverlay status={status} />
<StatusMedia status={status} />
</div>
)}
{quote}
</Stack>
);
if (onClick) {
if (status.content) {
output.push(
<Markup
ref={node}
tabIndex={0}
key='content'
className={className}
direction={direction}
lang={status.language || undefined}
size={textSize}
>
{parsedContent}
</Markup>,
);
}
if (collapsed) {
output.push(<ReadMoreButton onClick={onClick} key='read-more' quote={isQuote} poll={hasPoll} />);
}
if (status.poll_id) {
output.push(<Poll id={status.poll_id} key='poll' status={status} language={statusMeta.currentLanguage} />);
}
if (translatable) {
output.push(<TranslateButton status={status} key='translate' />);
}
if (media) {
output.push(media);
}
if (hashtags.length) {
output.push(<HashtagsBar key='hashtags' hashtags={hashtags} />);
}
} else {
quote = <QuotedStatus statusId={status.quote_id} />;
if (status.content) {
output.push(
<Markup
ref={node}
tabIndex={0}
key='content'
className={className}
direction={direction}
lang={status.language || undefined}
size={textSize}
>
{parsedContent}
</Markup>,
);
}
if (collapsed) {
output.push(<ReadMoreButton onClick={() => {}} key='read-more' quote={isQuote} preview={preview} />);
}
if (status.poll_id) {
output.push(<Poll id={status.poll_id} key='poll' status={status} language={statusMeta.currentLanguage} />);
}
if (translatable) {
output.push(<TranslateButton status={status} />);
}
if (media) {
output.push(media);
}
if (hashtags.length) {
output.push(<HashtagsBar key='hashtags' hashtags={hashtags} />);
}
}
}
const media = withMedia && ((quote || status.card || status.media_attachments.length > 0)) && (
<Stack space={4} key='media'>
{(status.media_attachments.length > 0 || (status.card && !quote)) && (
<div className='relative'>
<SensitiveContentOverlay status={status} />
<StatusMedia status={status} />
</div>
)}
{quote}
</Stack>
);
if (onClick) {
if (status.content) {
output.push(
<Markup
ref={node}
tabIndex={0}
key='content'
className={className}
direction={direction}
lang={status.language || undefined}
size={textSize}
>
{parsedContent}
</Markup>,
);
}
const hasPoll = !!status.poll_id;
if (collapsed) {
output.push(<ReadMoreButton onClick={onClick} key='read-more' quote={isQuote} poll={hasPoll} />);
}
if (status.poll_id) {
output.push(<Poll id={status.poll_id} key='poll' status={status} language={statusMeta.currentLanguage} />);
}
if (translatable) {
output.push(<TranslateButton status={status} key='translate' />);
}
if (media) {
output.push(media);
}
if (hashtags.length) {
output.push(<HashtagsBar key='hashtags' hashtags={hashtags} />);
}
return <Stack space={4} className={clsx({ 'bg-gray-100 dark:bg-primary-800 rounded-md p-4': hasPoll })}>{output}</Stack>;
} else {
if (status.content) {
output.push(
<Markup
ref={node}
tabIndex={0}
key='content'
className={className}
direction={direction}
lang={status.language || undefined}
size={textSize}
>
{parsedContent}
</Markup>,
);
}
if (collapsed) {
output.push(<ReadMoreButton onClick={() => {}} key='read-more' quote={isQuote} preview={preview} />);
}
if (status.poll_id) {
output.push(<Poll id={status.poll_id} key='poll' status={status} language={statusMeta.currentLanguage} />);
}
if (translatable) {
output.push(<TranslateButton status={status} />);
}
if (media) {
output.push(media);
}
if (hashtags.length) {
output.push(<HashtagsBar key='hashtags' hashtags={hashtags} />);
}
return <>{output}</>;
}
});

View File

@ -72,8 +72,10 @@ const Accordion: React.FC<IAccordion> = ({ headline, children, menu, expanded =
</button>
)}
<Icon
src={expanded ? require('@tabler/icons/outline/chevron-up.svg') : require('@tabler/icons/outline/chevron-down.svg')}
className='size-5 text-gray-700 dark:text-gray-600'
src={require('@tabler/icons/outline/chevron-down.svg')}
className={clsx('size-5 text-gray-700 transition-transform dark:text-gray-600', {
'rotate-180': expanded,
})}
/>
</HStack>
</button>

View File

@ -16,6 +16,8 @@ interface IButton extends Pick<
block?: boolean;
/** URL to an SVG icon to render inside the button. */
icon?: string;
/** Class name to apply to the icon element inside the button. */
iconClassName?: string;
/** URL to an SVG icon to render inside the button next to the text. */
secondaryIcon?: string;
/** A predefined button size. */
@ -36,6 +38,7 @@ const Button = React.forwardRef<HTMLButtonElement, IButton>(({
children,
disabled = false,
icon,
iconClassName,
secondaryIcon,
onClick,
size = 'md',
@ -72,7 +75,7 @@ const Button = React.forwardRef<HTMLButtonElement, IButton>(({
type={type}
data-testid='button'
>
{icon ? <Icon src={icon} className='size-4' /> : null}
{icon ? <Icon src={icon} className={clsx('size-4', iconClassName)} /> : null}
{body && (
<span>{body}</span>

View File

@ -1,3 +1,4 @@
import clsx from 'clsx';
import React, { HTMLAttributes } from 'react';
import HStack from 'pl-fe/components/ui/hstack';
@ -68,8 +69,10 @@ const ChatPaneHeader = (props: IChatPaneHeader) => {
<IconButton
onClick={onToggle}
src={isOpen ? require('@tabler/icons/outline/chevron-down.svg') : require('@tabler/icons/outline/chevron-up.svg')}
iconClassName='h-5 w-5 text-gray-600'
src={require('@tabler/icons/outline/chevron-up.svg')}
iconClassName={clsx('size-5 text-gray-600 transition-transform', {
'rotate-180': isOpen,
})}
/>
</HStack>
</HStack>