nicolium: reduce HStack/Stack usage

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2026-03-12 10:44:54 +01:00
parent b3430bbedb
commit 9d1b8104a9
18 changed files with 73 additions and 94 deletions

View File

@@ -7,7 +7,6 @@ import { useIntl, FormattedMessage } from 'react-intl';
import Badge from '@/components/badge';
import Card, { CardBody } from '@/components/ui/card';
import Icon from '@/components/ui/icon';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import ActionButton from '@/features/ui/components/action-button';
import { isTimezoneLabel } from '@/features/ui/components/profile-field';
@@ -153,7 +152,7 @@ const AccountHoverCard: React.FC<IAccountHoverCard> = ({ visible = true }) => {
className='relative isolate overflow-hidden black:rounded-xl black:border black:border-gray-800'
>
<CardBody>
<Stack space={2}>
<div className='flex flex-col gap-2'>
<UserPanel
accountId={account.id}
action={<ActionButton account={account} small />}
@@ -210,7 +209,7 @@ const AccountHoverCard: React.FC<IAccountHoverCard> = ({ visible = true }) => {
<ParsedContent html={account.note} emojis={account.emojis} />
</Text>
)}
</Stack>
</div>
{followedBy && (
<div className='absolute left-2 top-2'>

View File

@@ -7,10 +7,8 @@ import HoverAccountWrapper from '@/components/accounts/hover-account-wrapper';
import VerificationBadge from '@/components/accounts/verification-badge';
import Avatar from '@/components/ui/avatar';
import Emoji from '@/components/ui/emoji';
import HStack from '@/components/ui/hstack';
import Icon from '@/components/ui/icon';
import IconButton from '@/components/ui/icon-button';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import Emojify from '@/features/emoji/emojify';
import ActionButton from '@/features/ui/components/action-button';
@@ -347,10 +345,11 @@ const Account = ({
ref={overflowRef}
>
<div>
<HStack
alignItems={withAccountNote || note ? 'top' : 'center'}
space={3}
className='max-w-full'
<div
className={clsx(
'flex max-w-full items-center gap-3',
withAccountNote || note ? 'items-start' : 'items-center',
)}
>
{withAvatar &&
(disableUserProvidedMedia ? (
@@ -414,7 +413,8 @@ const Account = ({
</LinkEl>
</ProfilePopper>
<Stack space={withAccountNote || note ? 1 : 0}>
<div className='flex flex-col gap-1'>
{' '}
<div className='flex items-center gap-1'>
<Text theme='muted' size='sm' direction='ltr' truncate>
@{username}
@@ -490,7 +490,6 @@ const Account = ({
{items}
</div>
{note ? (
<Text size='sm' className='mr-2'>
{note}
@@ -510,9 +509,9 @@ const Account = ({
</Text>
)
)}
</Stack>
</div>
</div>
</HStack>
</div>
<div ref={actionRef}>{renderAction()}</div>
</div>

View File

@@ -1,7 +1,6 @@
import React from 'react';
import { FormattedDate } from 'react-intl';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import { useFeatures } from '@/hooks/use-features';
import { getTextDirection } from '@/utils/rtl';
@@ -36,7 +35,7 @@ const Announcement: React.FC<IAnnouncement> = ({ announcement, emojiMap }) => {
const direction = getTextDirection(announcement.content);
return (
<Stack className='w-full' space={2}>
<div className='flex w-full flex-col gap-2'>
{hasTimeRange && (
<Text theme='muted' direction={direction}>
<FormattedDate
@@ -70,7 +69,7 @@ const Announcement: React.FC<IAnnouncement> = ({ announcement, emojiMap }) => {
emojiMap={emojiMap}
/>
)}
</Stack>
</div>
);
};

View File

@@ -1,6 +1,5 @@
import React from 'react';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import Emojify from '@/features/emoji/emojify';
import GroupHeaderImage from '@/features/group/components/group-header-image';
@@ -21,17 +20,17 @@ const GroupCard: React.FC<IGroupCard> = ({ groupId }) => {
if (!group) return null;
return (
<Stack
className='relative h-[240px] rounded-lg border border-solid border-gray-300 bg-white black:bg-black dark:border-primary-800 dark:bg-primary-900'
<div
className='relative flex h-[240px] flex-col rounded-lg border border-solid border-gray-300 bg-white black:bg-black dark:border-primary-800 dark:bg-primary-900'
data-testid='group-card'
>
{/* Group Cover Image */}
<Stack grow className='relative basis-1/2 rounded-t-lg bg-primary-100 dark:bg-gray-800'>
<div className='relative flex grow basis-1/2 flex-col rounded-t-lg bg-primary-100 dark:bg-gray-800'>
<GroupHeaderImage
group={group}
className='absolute inset-0 size-full rounded-t-lg object-cover'
/>
</Stack>
</div>
{/* Group Avatar */}
<div className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'>
@@ -39,7 +38,7 @@ const GroupCard: React.FC<IGroupCard> = ({ groupId }) => {
</div>
{/* Group Info */}
<Stack alignItems='center' justifyContent='end' grow className='basis-1/2 py-4' space={0.5}>
<div className='flex grow basis-1/2 flex-col items-center justify-end gap-0.5 py-4'>
<div className='flex items-center gap-1.5'>
<Text size='lg' weight='bold'>
<Emojify text={group.display_name} emojis={group.emojis} />
@@ -51,8 +50,8 @@ const GroupCard: React.FC<IGroupCard> = ({ groupId }) => {
<GroupPrivacy group={group} />
<GroupMemberCount group={group} />
</div>
</Stack>
</Stack>
</div>
</div>
);
};

View File

@@ -6,7 +6,6 @@ import ScrollableList from '@/components/scrollable-list';
import Button from '@/components/ui/button';
import Column from '@/components/ui/column';
import HStack from '@/components/ui/hstack';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import { useFeatures } from '@/hooks/use-features';
import { useDeleteFilter, useFilters } from '@/queries/settings/use-filters';
@@ -71,8 +70,8 @@ const FiltersPage = () => {
>
{filters.map((filter) => (
<div key={filter.id} className='rounded-lg bg-gray-100 p-4 dark:bg-primary-800'>
<Stack space={2}>
<Stack className='grow' space={1}>
<div className='flex flex-col gap-2'>
<div className='flex grow flex-col gap-1'>
<Text weight='medium'>
<FormattedMessage
id='filters.filters_list_phrases_label'
@@ -137,7 +136,7 @@ const FiltersPage = () => {
</Text>
)}
</HStack>
</Stack>
</div>
<HStack space={2} justifyContent='end'>
<Button theme='primary' to='/filters/$filterId' params={{ filterId: filter.id }}>
<FormattedMessage id='column.filters.edit' defaultMessage='Edit filter' />
@@ -146,7 +145,7 @@ const FiltersPage = () => {
<FormattedMessage id='column.filters.delete' defaultMessage='Delete' />
</Button>
</HStack>
</Stack>
</div>
</div>
))}
</ScrollableList>

View File

@@ -4,7 +4,6 @@ import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import ScrollableList from '@/components/scrollable-list';
import Column from '@/components/ui/column';
import Stack from '@/components/ui/stack';
import AccountContainer from '@/containers/account-container';
import { useMutes } from '@/queries/account-lists/use-blocks';
@@ -19,7 +18,7 @@ const MutesPage: React.FC = () => {
return (
<Column label={intl.formatMessage(messages.heading)}>
<Stack space={4}>
<div className='flex flex-col gap-4'>
<ScrollableList
itemClassName={clsx('pb-4', { 'last:pb-0': !hasNextPage })}
scrollKey='mutes'
@@ -42,7 +41,7 @@ const MutesPage: React.FC = () => {
/>
))}
</ScrollableList>
</Stack>
</div>
</Column>
);
};

View File

@@ -7,11 +7,9 @@ import Button from '@/components/ui/button';
import Card, { CardTitle } from '@/components/ui/card';
import Column from '@/components/ui/column';
import Form from '@/components/ui/form';
import HStack from '@/components/ui/hstack';
import Icon from '@/components/ui/icon';
import IconButton from '@/components/ui/icon-button';
import Input from '@/components/ui/input';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import { useTextField } from '@/hooks/forms/use-text-field';
import {
@@ -58,7 +56,7 @@ const NewFeedForm: React.FC = () => {
return (
<Form onSubmit={handleSubmit}>
<HStack space={2} alignItems='center'>
<div className='flex items-center gap-2'>
<label className='grow'>
<span style={{ display: 'none' }}>{label}</span>
@@ -71,7 +69,7 @@ const NewFeedForm: React.FC = () => {
defaultMessage='Subscribe'
/>
</Button>
</HStack>
</div>
</Form>
);
};
@@ -96,7 +94,7 @@ const RssFeedSubscriptions = () => {
return (
<Column label={intl.formatMessage(messages.heading)}>
<Stack space={4}>
<div className='flex flex-col gap-4'>
<CardTitle
title={
<FormattedMessage
@@ -121,18 +119,18 @@ const RssFeedSubscriptions = () => {
<ListItem
key={feed.id}
label={
<HStack className='w-full' alignItems='center' space={2}>
<div className='flex w-full items-center gap-2'>
{feed.image_url ? (
<Avatar size={40} src={feed.image_url} />
) : (
<Icon src={require('@phosphor-icons/core/regular/rss.svg')} size={40} />
)}
<Stack className='flex-1'>
<div className='flex flex-1 flex-col'>
<span>{feed.title}</span>
<Text size='sm' theme='muted' truncate>
{feed.url}
</Text>
</Stack>
</div>
<IconButton
onClick={handleDelete(feed.url)}
disabled={isPending}
@@ -140,7 +138,7 @@ const RssFeedSubscriptions = () => {
src={require('@phosphor-icons/core/regular/x.svg')}
title={intl.formatMessage(messages.deleteFeed)}
/>
</HStack>
</div>
}
/>
))}
@@ -152,7 +150,7 @@ const RssFeedSubscriptions = () => {
</Card>
)
)}
</Stack>
</div>
</Column>
);
};

View File

@@ -7,11 +7,9 @@ import Button from '@/components/ui/button';
import Column from '@/components/ui/column';
import Emoji from '@/components/ui/emoji';
import Form from '@/components/ui/form';
import HStack from '@/components/ui/hstack';
import Icon from '@/components/ui/icon';
import Input from '@/components/ui/input';
import Spinner from '@/components/ui/spinner';
import Stack from '@/components/ui/stack';
import { useTextField } from '@/hooks/forms/use-text-field';
import { useFeatures } from '@/hooks/use-features';
import {
@@ -75,7 +73,7 @@ const NewFolderForm: React.FC<INewFolderForm> = ({ search, onChange }) => {
return (
<Form onSubmit={handleSubmit}>
<HStack space={2} alignItems='center'>
<div className='flex items-center gap-2'>
<Input
outerClassName='grow'
type='text'
@@ -89,7 +87,7 @@ const NewFolderForm: React.FC<INewFolderForm> = ({ search, onChange }) => {
<Button disabled={isPending} type='submit' theme='primary'>
<FormattedMessage id='bookmark_folders.new.create_title' defaultMessage='Add folder' />
</Button>
</HStack>
</div>
</Form>
);
};
@@ -113,7 +111,7 @@ const BookmarkFoldersPage: React.FC = () => {
return (
<Column label={intl.formatMessage(messages.heading)}>
<Stack space={4}>
<div className='flex flex-col gap-4'>
<NewFolderForm />
<List>
@@ -121,7 +119,7 @@ const BookmarkFoldersPage: React.FC = () => {
to='/bookmarks/$folderId'
params={{ folderId: 'all' }}
label={
<HStack alignItems='center' space={2}>
<div className='flex items-center gap-2'>
<Icon src={require('@phosphor-icons/core/regular/bookmarks.svg')} size={20} />
<span>
<FormattedMessage
@@ -129,7 +127,7 @@ const BookmarkFoldersPage: React.FC = () => {
defaultMessage='All bookmarks'
/>
</span>
</HStack>
</div>
}
/>
{bookmarkFolders?.map((folder) => (
@@ -138,7 +136,7 @@ const BookmarkFoldersPage: React.FC = () => {
to='/bookmarks/$folderId'
params={{ folderId: folder.id }}
label={
<HStack alignItems='center' space={2}>
<div className='flex items-center gap-2'>
{folder.emoji ? (
<Emoji
emoji={folder.emoji}
@@ -152,12 +150,12 @@ const BookmarkFoldersPage: React.FC = () => {
/>
)}
<span>{folder.name}</span>
</HStack>
</div>
}
/>
))}
</List>
</Stack>
</div>
</Column>
);
};

View File

@@ -11,7 +11,6 @@ import ScrollableList from '@/components/scrollable-list';
import StatusContent from '@/components/statuses/status-content';
import Button from '@/components/ui/button';
import Column from '@/components/ui/column';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import AccountContainer from '@/containers/account-container';
import { buildLink } from '@/features/notifications/components/notification';
@@ -87,7 +86,7 @@ const InteractionRequestStatus: React.FC<IInteractionRequestStatus> = ({
if (!status) return null;
return (
<Stack className='relative py-2' space={2}>
<div className='relative flex flex-col gap-2 py-2'>
{hasReply && (
<div className='absolute left-5 top-[62px] z-[1] block h-[calc(100%-58px)] w-0.5 bg-gray-200 black:bg-gray-800 dark:bg-primary-800 rtl:left-auto rtl:right-5' />
)}
@@ -100,12 +99,12 @@ const InteractionRequestStatus: React.FC<IInteractionRequestStatus> = ({
action={actions ?? <></>}
/>
<Stack space={2} className={clsx(hasReply && 'pl-[54px]')}>
<div className={clsx('flex flex-col gap-2', hasReply && 'pl-[54px]')}>
<StatusContent status={status} preview={!isReply} />
{status.media_attachments.length > 0 && <AttachmentThumbs status={status} />}
</Stack>
</Stack>
</div>
</div>
);
};
@@ -227,7 +226,7 @@ const InteractionRequest: React.FC<IInteractionRequest> = ({
return (
<Hotkeys handlers={handlers} className='notification focusable' tabIndex={0}>
<div className='focusable p-4'>
<Stack space={2}>
<div className='flex flex-col gap-2'>
<div>
<div className='flex items-center gap-3'>
<div className='flex justify-end' style={{ flexBasis: avatarSize }}>
@@ -268,7 +267,7 @@ const InteractionRequest: React.FC<IInteractionRequest> = ({
{interactionRequest.reply_id && (
<InteractionRequestStatus id={interactionRequest.reply_id} isReply actions={actions} />
)}
</Stack>
</div>
</div>
</Hotkeys>
);

View File

@@ -3,7 +3,6 @@ import { defineMessages, useIntl } from 'react-intl';
import { cancelEventCompose } from '@/actions/events';
import Column from '@/components/ui/column';
import Stack from '@/components/ui/stack';
import Tabs from '@/components/ui/tabs';
import { EditEvent } from '@/features/compose-event/tabs/edit-event';
import { ManagePendingParticipants } from '@/features/compose-event/tabs/manage-pending-participants';
@@ -53,14 +52,14 @@ const EditEventPage = () => {
return (
<Column label={intl.formatMessage(messages.manageEvent)}>
<Stack space={2}>
<div className='flex flex-col gap-2'>
{renderTabs()}
{tab === 'edit' ? (
<EditEvent statusId={statusId} />
) : (
<ManagePendingParticipants statusId={statusId} />
)}
</Stack>
</div>
</Column>
);
};

View File

@@ -4,7 +4,6 @@ import { FormattedMessage } from 'react-intl';
import MissingIndicator from '@/components/missing-indicator';
import ScrollableList from '@/components/scrollable-list';
import Tombstone from '@/components/statuses/tombstone';
import Stack from '@/components/ui/stack';
import PlaceholderStatus from '@/features/placeholder/components/placeholder-status';
import ThreadStatus from '@/features/status/components/thread-status';
import PendingStatus from '@/features/ui/components/pending-status';
@@ -94,7 +93,7 @@ const EventDiscussionPage: React.FC = () => {
}
return (
<Stack space={2}>
<div className='flex flex-col gap-2'>
{me && (
<div className='border-b border-solid border-gray-200 p-2 pt-0 dark:border-gray-800'>
<ComposeForm id={`reply:${status.id}`} event={status.id} transparent />
@@ -116,7 +115,7 @@ const EventDiscussionPage: React.FC = () => {
{children}
</ScrollableList>
</div>
</Stack>
</div>
);
};

View File

@@ -7,7 +7,6 @@ import DropdownMenu, { type Menu } from '@/components/dropdown-menu';
import MissingIndicator from '@/components/missing-indicator';
import PullToRefresh from '@/components/pull-to-refresh';
import Column from '@/components/ui/column';
import Stack from '@/components/ui/stack';
import PlaceholderStatus from '@/features/placeholder/components/placeholder-status';
import Thread from '@/features/status/components/thread';
import { statusRoute } from '@/features/ui/router';
@@ -138,7 +137,7 @@ const StatusPage: React.FC = () => {
};
return (
<Stack space={4}>
<div className='flex flex-col gap-4'>
<Column
label={intl.formatMessage(titleMessage())}
action={
@@ -158,7 +157,7 @@ const StatusPage: React.FC = () => {
/>
</PullToRefresh>
</Column>
</Stack>
</div>
);
};

View File

@@ -5,8 +5,6 @@ import { FormattedMessage } from 'react-intl';
import { GroupTimelineColumn } from '@/columns/timeline';
import Avatar from '@/components/ui/avatar';
import HStack from '@/components/ui/hstack';
import Stack from '@/components/ui/stack';
import { groupTimelineRoute } from '@/features/ui/router';
import { ComposeForm } from '@/features/ui/util/async-components';
import { useDraggedFiles } from '@/hooks/use-dragged-files';
@@ -45,14 +43,14 @@ const GroupTimelinePage: React.FC = () => {
}
return (
<Stack space={2}>
<div clsasName='flex flex-col gap-2'>
{canComposeGroupStatus && (
<div className='border-b border-solid border-gray-200 py-6 dark:border-gray-800'>
<HStack
<div
ref={composer}
alignItems='start'
space={2}
className={clsx('relative rounded-xl transition', {
className={clsx('relative flex items-start gap-2 rounded-xl transition', {
'z-[99] border-2 border-dashed border-primary-600 p-4': isDragging,
'ring-2 ring-primary-600 ring-offset-2': isDraggedOver,
})}
@@ -67,7 +65,7 @@ const GroupTimelinePage: React.FC = () => {
</Link>
<ComposeForm id={composeId} shouldCondense group={groupId} withAvatar transparent />
</HStack>
</div>
</div>
)}
@@ -82,7 +80,7 @@ const GroupTimelinePage: React.FC = () => {
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
// showGroup={falsse}
/>
</Stack>
</div>
);
};

View File

@@ -4,7 +4,6 @@ import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { HomeTimelineColumn } from '@/columns/timeline';
import { Link } from '@/components/link';
import Column from '@/components/ui/column';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import { useFeatures } from '@/hooks/use-features';
import { useInstance } from '@/hooks/use-instance';
@@ -50,7 +49,7 @@ const HomeTimelinePage: React.FC = () => {
<Column className='py-0' label={intl.formatMessage(messages.title)} withHeader={false}>
<HomeTimelineColumn
emptyMessageText={
<Stack space={1}>
<div className='flex flex-col gap-1'>
<Text size='xl' weight='medium' align='center'>
<FormattedMessage
id='empty_column.home.title'
@@ -84,7 +83,7 @@ const HomeTimelinePage: React.FC = () => {
/>
</Text>
)}
</Stack>
</div>
}
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
/>

View File

@@ -7,7 +7,6 @@ import Markup from '@/components/markup';
import { ParsedContent } from '@/components/statuses/parsed-content';
import Button from '@/components/ui/button';
import Column from '@/components/ui/column';
import Stack from '@/components/ui/stack';
import { useInstance } from '@/hooks/use-instance';
import { useRegistrationStatus } from '@/hooks/use-registration-status';
import { About } from '@/pages/utils/about';
@@ -35,7 +34,7 @@ const SiteBanner: React.FC = () => {
const instance = useInstance();
return (
<Stack space={6}>
<div className='flex flex-col gap-6'>
<LogoText className='-my-5' dir={getTextDirection(instance.title)}>
{instance.title}
</LogoText>
@@ -45,7 +44,7 @@ const SiteBanner: React.FC = () => {
<ParsedContent html={instance.description} />
</Markup>
)}
</Stack>
</div>
);
};

View File

@@ -3,7 +3,6 @@ import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import Accordion from '@/components/ui/accordion';
import Column from '@/components/ui/column';
import Stack from '@/components/ui/stack';
import SiteWallet from '@/features/crypto-donate/components/site-wallet';
import { useInstance } from '@/hooks/use-instance';
@@ -19,7 +18,7 @@ const CryptoDonatePage: React.FC = (): React.JSX.Element => {
return (
<Column label={intl.formatMessage(messages.heading)} withHeader>
<Stack space={5}>
<div className='flex flex-col gap-5'>
<Accordion
headline={
<FormattedMessage
@@ -38,7 +37,7 @@ const CryptoDonatePage: React.FC = (): React.JSX.Element => {
</Accordion>
<SiteWallet />
</Stack>
</div>
</Column>
);
};

View File

@@ -4,7 +4,6 @@ import { FormattedMessage } from 'react-intl';
import Button from '@/components/ui/button';
import Card, { CardTitle } from '@/components/ui/card';
import Column from '@/components/ui/column';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import LinkFooter from '@/features/ui/components/link-footer';
import sourceCode from '@/utils/code';
@@ -14,7 +13,7 @@ import { LogoText } from '../timelines/landing-timeline';
const LandingPage = () => (
<>
<Column withHeader={false}>
<Stack space={4}>
<div className='flex flex-col gap-4'>
<LogoText>
<FormattedMessage id='landing.logo' defaultMessage='Nicolium' />
</LogoText>
@@ -120,11 +119,11 @@ const LandingPage = () => (
/>
</Text>
</Card>
</Stack>
</div>
</Column>
<Stack space={4} className='mt-4 px-4 xl:hidden'>
<div className='mt-4 flex flex-col gap-4 px-4 xl:hidden'>
<LinkFooter />
</Stack>
</div>
</>
);

View File

@@ -3,7 +3,6 @@ import { defineMessages, useIntl } from 'react-intl';
import Column from '@/components/ui/column';
import Divider from '@/components/ui/divider';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import LinkFooter from '@/features/ui/components/link-footer';
import PromoPanel from '@/features/ui/components/panels/promo-panel';
@@ -19,13 +18,13 @@ const ServerInfoPage = () => {
return (
<Column label={intl.formatMessage(messages.heading)}>
<Stack space={4}>
<Stack>
<div className='flex flex-col gap-4'>
<div className='flex flex-col'>
<Text size='lg' weight='medium'>
{instance.title}
</Text>
<Text theme='muted'>{instance.description}</Text>
</Stack>
</div>
<Divider />
@@ -34,7 +33,7 @@ const ServerInfoPage = () => {
<Divider />
<LinkFooter />
</Stack>
</div>
</Column>
);
};