nicolium: wip style migrations

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2026-02-13 20:35:52 +01:00
parent d7be398172
commit 34469f3376
4 changed files with 94 additions and 52 deletions

View File

@ -68,7 +68,7 @@ const Poll: React.FC<IPoll> = ({ id, status, language, truncate }): JSX.Element
return ( return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions // eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div onClick={e => e.stopPropagation()}> <div className='⁂-poll' onClick={e => e.stopPropagation()}>
{!showResults && poll.multiple && ( {!showResults && poll.multiple && (
<Text className='mb-4' theme='muted' size='sm'> <Text className='mb-4' theme='muted' size='sm'>
<FormattedMessage id='poll.choose_multiple' defaultMessage="Choose as many as you'd like." /> <FormattedMessage id='poll.choose_multiple' defaultMessage="Choose as many as you'd like." />

View File

@ -2,10 +2,8 @@ import clsx from 'clsx';
import React, { useState, useRef, useLayoutEffect, useMemo } from 'react'; import React, { useState, useRef, useLayoutEffect, useMemo } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import Icon from '@/components/icon'; import Icon from '@/components/ui/icon';
import Button from '@/components/ui/button';
import Stack from '@/components/ui/stack'; import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import Emojify from '@/features/emoji/emojify'; import Emojify from '@/features/emoji/emojify';
import QuotedStatus from '@/features/status/containers/quoted-status-container'; import QuotedStatus from '@/features/status/containers/quoted-status-container';
import { useFrontendConfig } from '@/hooks/use-frontend-config'; import { useFrontendConfig } from '@/hooks/use-frontend-config';
@ -35,28 +33,15 @@ const BIG_EMOJI_LIMIT = 10;
interface IReadMoreButton { interface IReadMoreButton {
onClick?: React.MouseEventHandler; onClick?: React.MouseEventHandler;
quote?: boolean;
poll?: boolean;
preview?: boolean; preview?: boolean;
} }
/** Button to expand a truncated status (due to too much content) */ /** Button to expand a truncated status (due to too much content) */
const ReadMoreButton: React.FC<IReadMoreButton> = ({ onClick, quote, poll, preview }) => ( const ReadMoreButton: React.FC<IReadMoreButton> = ({ onClick, preview }) => (
<div <div className='⁂-read-more-button__container'>
className={clsx('relative', { <div className='⁂-read-more-button__gradient' />
'-mt-4': !preview,
'-mt-2': preview,
})}
>
<div
className={clsx('pointer-events-none absolute -top-16 h-16 w-full bg-gradient-to-b from-transparent', {
'to-white black:to-black dark:to-primary-900': !poll,
'to-gray-100 dark:to-primary-800': poll,
'group-hover:to-gray-100 black:group-hover:to-gray-800 dark:group-hover:to-gray-800': quote,
})}
/>
{!preview && ( {!preview && (
<button className='flex items-center border-0 bg-transparent p-0 pt-2 text-gray-900 hover:underline active:underline dark:text-gray-300' onClick={onClick}> <button className='⁂-read-more-button' onClick={onClick}>
<FormattedMessage id='status.read_more' defaultMessage='Read more' /> <FormattedMessage id='status.read_more' defaultMessage='Read more' />
<Icon className='inline-block size-5' src={require('@phosphor-icons/core/regular/caret-right.svg')} /> <Icon className='inline-block size-5' src={require('@phosphor-icons/core/regular/caret-right.svg')} />
</button> </button>
@ -180,8 +165,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
}, [spoilerText]); }, [spoilerText]);
const direction = getTextDirection(status.search_index); const direction = getTextDirection(status.search_index);
const className = useMemo(() => clsx('relative text-ellipsis break-words text-gray-900 focus:outline-none dark:text-gray-100', { const className = useMemo(() => clsx('⁂-status-content', {
'cursor-pointer': onClick,
'overflow-hidden': collapsed, 'overflow-hidden': collapsed,
'max-h-[200px]': collapsed && !isQuote && !preview, 'max-h-[200px]': collapsed && !isQuote && !preview,
'max-h-[120px]': collapsed && isQuote, 'max-h-[120px]': collapsed && isQuote,
@ -190,7 +174,10 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
'max-h-[202px]': collapsable && collapsed === null && isQuote, 'max-h-[202px]': collapsable && collapsed === null && isQuote,
'max-h-[82px]': collapsed === null && preview, 'max-h-[82px]': collapsed === null && preview,
'leading-normal big-emoji': onlyEmoji, 'leading-normal big-emoji': onlyEmoji,
'⁂-status-content__expanded': !collapsable, '⁂-status-content--expanded': !collapsable,
'⁂-status-content--quote': isQuote,
'⁂-status-content--preview': preview,
'⁂-status-content--poll': !!status.poll_id,
}), [collapsed, onlyEmoji]); }), [collapsed, onlyEmoji]);
const expandable = !displaySpoilers; const expandable = !displaySpoilers;
@ -199,41 +186,31 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
if (spoilerText) { if (spoilerText) {
output.push( output.push(
<Text <p
className={clsx('⁂-status-title', {
'⁂-status-title--clamp': !expanded && lineClamp,
})}
key='spoiler' key='spoiler'
size='2xl'
weight='medium'
aria-expanded={expanded} aria-expanded={expanded}
{...(expandable ? { onClick: toggleExpanded, role: 'button' } : {})} {...(expandable ? { onClick: toggleExpanded, role: 'button' } : {})}
> >
<span className={clsx({ 'line-clamp-3': !expanded && lineClamp })} ref={spoilerNode}> <span ref={spoilerNode}>
<Emojify text={spoilerText} emojis={status.emojis} /> <Emojify text={spoilerText} emojis={status.emojis} />
</span> </span>
{expandable && ( {expandable && (
<Button <button onClick={toggleExpanded}>
className='ml-2 align-middle' <Icon src={require('@phosphor-icons/core/regular/caret-down.svg')} />
type='button' <span>
theme='muted' {expanded
size='xs' ? <FormattedMessage id='status.spoiler.collapse' defaultMessage='Collapse' />
onClick={toggleExpanded} : <FormattedMessage id='status.spoiler.expand' defaultMessage='Expand' />}
icon={require('@phosphor-icons/core/regular/caret-down.svg')} </span>
iconClassName={clsx( </button>
'transition-transform', {
'rotate-180': expanded,
},
)}
>
{expanded
? <FormattedMessage id='status.spoiler.collapse' defaultMessage='Collapse' />
: <FormattedMessage id='status.spoiler.expand' defaultMessage='Expand' />}
</Button>
)} )}
</Text>, </p>,
); );
} }
const hasPoll = !!status.poll_id;
if (!expandable || expanded) { if (!expandable || expanded) {
let quote; let quote;
@ -283,7 +260,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
} }
if (collapsed) { if (collapsed) {
output.push(<ReadMoreButton onClick={onClick} key='read-more' quote={isQuote} poll={hasPoll} preview={preview} />); output.push(<ReadMoreButton onClick={onClick} key='read-more' preview={preview} />);
} }
if (status.poll_id) { if (status.poll_id) {
@ -304,7 +281,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
} }
if (onClick) { if (onClick) {
return <Stack space={4} className={clsx({ 'bg-gray-100 dark:bg-primary-800 rounded-md p-4': hasPoll })}>{output}</Stack>; return <div className='⁂-status-content__container'>{output}</div>;
} else { } else {
return <>{output}</>; return <>{output}</>;
} }

View File

@ -150,7 +150,7 @@ $vertical-lr-langs: mn-mong, mnmong;
max-height: 209px; // roughly above 500 characters, readable max-height: 209px; // roughly above 500 characters, readable
overflow-x: hidden; // read more overflow-x: hidden; // read more
&.-status-content__expanded { &.-status-content--expanded {
max-width: unset; max-width: unset;
max-height: 50vh; max-height: 50vh;
overflow-x: auto; overflow-x: auto;

View File

@ -228,4 +228,69 @@
} }
} }
} }
} }
.-read-more-button {
@apply flex items-center border-0 bg-transparent p-0 pt-2 text-gray-900 hover:underline active:underline dark:text-gray-300;
&__gradient {
@apply pointer-events-none absolute -top-16 h-16 w-full bg-gradient-to-b from-transparent to-white black:to-black dark:to-primary-900;
}
&__container {
position: relative;
margin-top: -1rem;
}
}
.-status-content {
@apply relative text-ellipsis break-words text-gray-900 focus:outline-none dark:text-gray-100;
&:has(button) {
cursor: pointer;
}
&--preview + .-read-more-button__container {
margin-top: -0.5rem;
}
&--poll + .-read-more-button__container .-read-more-button__gradient {
@apply to-gray-100 dark:to-primary-800;
}
&--quote + .-read-more-button__container .-read-more-button__gradient {
@apply group-hover:to-gray-100 black:group-hover:to-gray-800 dark:group-hover:to-gray-800;
}
&__container {
display: flex;
flex-direction: column;
gap: 1rem;
&:has(.-poll) {
@apply bg-gray-100 dark:bg-primary-800 rounded-md p-4;
}
}
}
.-status-title {
@include mixins.text($size: 2xl, $weight: medium);
&--clamp span:first-child {
@apply line-clamp-3
}
button {
@include mixins.button($theme: muted, $size: xs);
margin-left: 0.5rem;
vertical-align: middle;
svg {
@apply size-4 transition-transform;
}
}
&[aria-expanded='true'] button svg {
transform: rotate(180deg);
}
}