0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-06 22:40:14 -05:00

Fixed separator showing even after the last list item (#21620)

close
https://linear.app/ghost/issue/AP-528/remove-unnecessary-separator-after-last-list-items

- Previously, in some components, we relied on CSS and all elements in
the list being the same type of an HTML element to show the separator.
Now, we've extracted the separator into its own component and place
after all items during mapping except the last one.
This commit is contained in:
Djordje Vlaisavljevic 2024-11-14 17:42:43 +00:00 committed by GitHub
parent 0aae3bb1fc
commit af54b21bf3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 118 additions and 95 deletions

View file

@ -8,6 +8,7 @@ import APAvatar, {AvatarBadge} from './global/APAvatar';
import ActivityItem, {type Activity} from './activities/ActivityItem';
import ArticleModal from './feed/ArticleModal';
import MainNavigation from './navigation/MainNavigation';
import Separator from './global/Separator';
import ViewProfileModal from './global/ViewProfileModal';
import getUsername from '../utils/get-username';
@ -184,28 +185,24 @@ const Activities: React.FC<ActivitiesProps> = ({}) => {
(isLoading === false && activities.length > 0) && (
<>
<div className='mt-8 flex w-full max-w-[560px] flex-col'>
{activities?.map(activity => (
<ActivityItem
key={activity.id}
url={getActivityUrl(activity) || getActorUrl(activity)}
onClick={() => handleActivityClick(activity)}
>
<APAvatar author={activity.actor} badge={getActivityBadge(activity)} />
<div className='min-w-0'>
<div className='truncate text-grey-600'>
<span className='mr-1 font-bold text-black'>{activity.actor.name}</span>
{getUsername(activity.actor)}
{activities?.map((activity, index) => (
<React.Fragment key={activity.id}>
<ActivityItem
url={getActivityUrl(activity) || getActorUrl(activity)}
onClick={() => handleActivityClick(activity)}
>
<APAvatar author={activity.actor} badge={getActivityBadge(activity)} />
<div className='min-w-0'>
<div className='truncate text-grey-600'>
<span className='mr-1 font-bold text-black'>{activity.actor.name}</span>
{getUsername(activity.actor)}
</div>
<div className=''>{getActivityDescription(activity)}</div>
{getExtendedDescription(activity)}
</div>
<div className=''>{getActivityDescription(activity)}</div>
{getExtendedDescription(activity)}
</div>
{/* <FollowButton
className='ml-auto'
following={isFollower(activity.actor.id)}
handle={getUsername(activity.actor)}
type='link'
/> */}
</ActivityItem>
</ActivityItem>
{index < activities.length - 1 && <Separator />}
</React.Fragment>
))}
</div>
<div ref={loadMoreRef} className='h-1'></div>

View file

@ -5,6 +5,7 @@ import FeedItem from './feed/FeedItem';
import MainNavigation from './navigation/MainNavigation';
import NiceModal from '@ebay/nice-modal-react';
import React, {useEffect, useRef} from 'react';
import Separator from './global/Separator';
import ViewProfileModal from './global/ViewProfileModal';
import getName from '../utils/get-name';
import getUsername from '../utils/get-username';
@ -94,7 +95,7 @@ const Inbox: React.FC<InboxProps> = ({}) => {
onCommentClick={() => handleViewContent(activity, true, updateActivity)}
/>
{index < activities.length - 1 && (
<div className="h-px w-full bg-grey-200"></div>
<Separator />
)}
</li>
))}
@ -107,29 +108,28 @@ const Inbox: React.FC<InboxProps> = ({}) => {
</ul>
</div>
<div className='sticky top-[135px] ml-auto w-full max-w-[300px] max-lg:hidden xxxl:sticky xxxl:right-[40px]'>
<h2 className='mb-2 text-lg font-semibold'>You might also like...</h2>
<h2 className='mb-2 text-lg font-semibold'>You might also like</h2>
{isLoadingSuggested ? (
<LoadingIndicator size="sm" />
) : (
<ul className='grow'>
{suggested.map((profile) => {
{suggested.map((profile, index) => {
const actor = profile.actor;
// const isFollowing = profile.isFollowing;
return (
<li key={actor.id}>
<ActivityItem url={actor.url} onClick={() => NiceModal.show(ViewProfileModal, {
profile: getUsername(actor),
onFollow: () => {},
onUnfollow: () => {}
})}>
<APAvatar author={actor} />
<div>
<div className='text-grey-600'>
<span className='mr-1 truncate font-bold text-black'>{getName(actor)}</span>
<div className='truncate text-sm'>{getUsername(actor)}</div>
<React.Fragment key={actor.id}>
<li key={actor.id}>
<ActivityItem url={actor.url} onClick={() => NiceModal.show(ViewProfileModal, {
profile: getUsername(actor),
onFollow: () => {},
onUnfollow: () => {}
})}>
<APAvatar author={actor} />
<div className='flex min-w-0 flex-col'>
<span className='block w-full truncate font-bold text-black'>{getName(actor)}</span>
<span className='block w-full truncate text-sm text-grey-600'>{getUsername(actor)}</span>
</div>
</div>
{/* <FollowButton
{/* <FollowButton
className='ml-auto'
following={isFollowing}
handle={getUsername(actor)}
@ -137,8 +137,10 @@ const Inbox: React.FC<InboxProps> = ({}) => {
onFollow={() => updateSuggestedProfile(actor.id!, {isFollowing: true})}
onUnfollow={() => updateSuggestedProfile(actor.id!, {isFollowing: false})}
/> */}
</ActivityItem>
</li>
</ActivityItem>
</li>
{index < suggested.length - 1 && <Separator />}
</React.Fragment>
);
})}
</ul>

View file

@ -8,6 +8,7 @@ import getName from '../utils/get-name';
import getUsername from '../utils/get-username';
import {ActorProperties} from '@tryghost/admin-x-framework/api/activitypub';
import Separator from './global/Separator';
import ViewProfileModal from './global/ViewProfileModal';
import {Button, Heading, List, NoValueLabel, Tab, TabView} from '@tryghost/admin-x-design-system';
import {handleViewContent} from '../utils/content-handlers';
@ -102,7 +103,7 @@ const Profile: React.FC<ProfileProps> = ({}) => {
onCommentClick={() => handleViewContent(activity, true)}
/>
{index < posts.length - 1 && (
<div className="h-px w-full bg-grey-200"></div>
<Separator />
)}
</li>
))}
@ -147,7 +148,7 @@ const Profile: React.FC<ProfileProps> = ({}) => {
onCommentClick={() => handleViewContent(activity, true)}
/>
{index < liked.length - 1 && (
<div className="h-px w-full bg-grey-200"></div>
<Separator />
)}
</li>
))}
@ -178,25 +179,28 @@ const Profile: React.FC<ProfileProps> = ({}) => {
</NoValueLabel>
) : (
<List>
{following.slice(0, visibleFollowing).map((item) => {
{following.slice(0, visibleFollowing).map((item, index) => {
return (
<ActivityItem
key={item.id}
url={item.url}
onClick={() => handleUserClick(item)}
>
<APAvatar author={item} />
<div>
<div className='text-grey-600'>
<span className='mr-1 font-bold text-black'>{getName(item)}</span>
<div className='text-sm'>{getUsername(item)}</div>
<React.Fragment key={item.id}>
<ActivityItem
key={item.id}
url={item.url}
onClick={() => handleUserClick(item)}
>
<APAvatar author={item} />
<div>
<div className='text-grey-600'>
<span className='mr-1 font-bold text-black'>{getName(item)}</span>
<div className='text-sm'>{getUsername(item)}</div>
</div>
</div>
</div>
{/* <Button className='ml-auto' color='grey' label='Unfollow' link={true} onClick={(e) => {
{/* <Button className='ml-auto' color='grey' label='Unfollow' link={true} onClick={(e) => {
e?.preventDefault();
alert('Implement me!');
}} /> */}
</ActivityItem>
</ActivityItem>
{index < following.length - 1 && <Separator />}
</React.Fragment>
);
})}
</List>
@ -226,21 +230,24 @@ const Profile: React.FC<ProfileProps> = ({}) => {
</NoValueLabel>
) : (
<List>
{followers.slice(0, visibleFollowers).map((item) => {
{followers.slice(0, visibleFollowers).map((item, index) => {
return (
<ActivityItem
key={item.id}
url={item.url}
onClick={() => handleUserClick(item)}
>
<APAvatar author={item} />
<div>
<div className='text-grey-600'>
<span className='mr-1 font-bold text-black'>{item.name || getName(item) || 'Unknown'}</span>
<div className='text-sm'>{getUsername(item)}</div>
<React.Fragment key={item.id}>
<ActivityItem
key={item.id}
url={item.url}
onClick={() => handleUserClick(item)}
>
<APAvatar author={item} />
<div>
<div className='text-grey-600'>
<span className='mr-1 font-bold text-black'>{item.name || getName(item) || 'Unknown'}</span>
<div className='text-sm'>{getUsername(item)}</div>
</div>
</div>
</div>
</ActivityItem>
</ActivityItem>
{index < followers.length - 1 && <Separator />}
</React.Fragment>
);
})}
</List>

View file

@ -12,6 +12,7 @@ import MainNavigation from './navigation/MainNavigation';
import NiceModal from '@ebay/nice-modal-react';
import ViewProfileModal from './global/ViewProfileModal';
import Separator from './global/Separator';
import {useSearchForUser, useSuggestedProfiles} from '../hooks/useActivityPubQueries';
interface SearchResultItem {
@ -103,13 +104,18 @@ const SuggestedAccounts: React.FC<{
<LoadingIndicator size='md'/>
</div>
)}
{profiles.map(profile => (
<SearchResult
key={profile.actor.id}
result={profile}
update={onUpdate}
/>
))}
{profiles.map((profile, index) => {
return (
<React.Fragment key={profile.actor.id}>
<SearchResult
key={profile.actor.id}
result={profile}
update={onUpdate}
/>
{index < profiles.length - 1 && <Separator />}
</React.Fragment>
);
})}
</>
);
};
@ -141,8 +147,8 @@ const Search: React.FC<SearchProps> = ({}) => {
return (
<>
<MainNavigation page='search' />
<div className='z-0 flex w-full flex-col items-center pt-8'>
<div className='relative flex w-full max-w-[560px] items-center '>
<div className='z-0 mx-auto flex w-full max-w-[560px] flex-col items-center pt-8'>
<div className='relative flex w-full items-center'>
<Icon className='absolute left-3 top-3 z-10' colorClass='text-grey-500' name='magnifying-glass' size='sm' />
<TextField
className='mb-6 mr-12 flex h-10 w-full items-center rounded-lg border border-transparent bg-grey-100 px-[33px] py-1.5 transition-colors focus:border-green focus:bg-white focus:outline-2 dark:border-transparent dark:bg-grey-925 dark:text-white dark:placeholder:text-grey-800 dark:focus:border-green dark:focus:bg-grey-950 tablet:mr-0'

View file

@ -23,7 +23,7 @@ const ActivityItem: React.FC<ActivityItemProps> = ({children, url = null, onClic
const childrenArray = React.Children.toArray(children);
const Item = (
<div className='relative flex w-full max-w-[560px] cursor-pointer flex-col border-b border-grey-100 before:absolute before:inset-x-[-8px] before:inset-y-[-1px] before:rounded-md before:bg-grey-50 before:opacity-0 before:transition-opacity hover:z-10 hover:cursor-pointer hover:border-b-transparent hover:before:opacity-100 dark:border-grey-950 dark:before:bg-grey-950' onClick={() => {
<div className='relative flex w-full max-w-[560px] cursor-pointer flex-col before:absolute before:inset-x-[-8px] before:inset-y-[-1px] before:rounded-md before:bg-grey-50 before:opacity-0 before:transition-opacity hover:z-10 hover:cursor-pointer hover:border-b-transparent hover:before:opacity-100 dark:before:bg-grey-950' onClick={() => {
if (!url && onClick) {
onClick();
}

View file

@ -0,0 +1,7 @@
import React from 'react';
const Separator: React.FC = () => {
return <div className='h-px w-full bg-grey-150 dark:bg-grey-950' />;
};
export default Separator;

View file

@ -13,6 +13,7 @@ import APAvatar from '../global/APAvatar';
import ActivityItem from '../activities/ActivityItem';
import FeedItem from '../feed/FeedItem';
import FollowButton from '../global/FollowButton';
import Separator from './Separator';
import getName from '../../utils/get-name';
import getUsername from '../../utils/get-username';
@ -81,23 +82,26 @@ const ActorList: React.FC<ActorListProps> = ({
</NoValueLabel>
) : (
<List>
{actorData.map(({actor, isFollowing}) => {
{actorData.map(({actor, isFollowing}, index) => {
return (
<ActivityItem key={actor.id} url={actor.url}>
<APAvatar author={actor} />
<div>
<div className='text-grey-600'>
<span className='mr-1 font-bold text-black'>{getName(actor)}</span>
<div className='text-sm'>{getUsername(actor)}</div>
<React.Fragment key={actor.id}>
<ActivityItem key={actor.id} url={actor.url}>
<APAvatar author={actor} />
<div>
<div className='text-grey-600'>
<span className='mr-1 font-bold text-black'>{getName(actor)}</span>
<div className='text-sm'>{getUsername(actor)}</div>
</div>
</div>
</div>
<FollowButton
className='ml-auto'
following={isFollowing}
handle={getUsername(actor)}
type='link'
/>
</ActivityItem>
<FollowButton
className='ml-auto'
following={isFollowing}
handle={getUsername(actor)}
type='link'
/>
</ActivityItem>
{index < actorData.length - 1 && <Separator />}
</React.Fragment>
);
})}
</List>
@ -188,7 +192,7 @@ const ViewProfileModal: React.FC<ViewProfileModalProps> = ({
onCommentClick={() => {}}
/>
{index < posts.length - 1 && (
<div className="h-px w-full bg-grey-200"></div>
<Separator />
)}
</div>
))}

View file

@ -29,7 +29,7 @@ const MainNavigation: React.FC<MainNavigationProps> = ({
</div>
<div className='col-[3/4] flex items-center justify-end gap-2 px-8'>
{page === 'home' &&
<div className='mr-3'>
<div>
<Tooltip content="Inbox">
<Button className='!px-2' icon='listview' iconColorClass={layout === 'inbox' ? 'text-black' : 'text-grey-400'} size='sm' onClick={setInbox} />
</Tooltip>