Skip to content

Commit f247a5b

Browse files
tsahimatsliahcursoragentrebelchrisgithub-actions[bot]
authored
fix: header, sidebar UI, animations & mobile header fix (#5456)
Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Chris Bongers <chrisbongers@gmail.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Tsahi Matsliah <tsahimatsliah@users.noreply.github.com> Co-authored-by: Chris Bongers <rebelchris@users.noreply.github.com>
1 parent 4143b3b commit f247a5b

File tree

21 files changed

+227
-226
lines changed

21 files changed

+227
-226
lines changed

packages/shared/src/components/Feed.module.css

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22
grid-template-columns: 100%;
33
}
44
.container {
5-
@screen laptopL {
5+
max-width: 100%;
6+
7+
@screen desktopL {
68
max-width: calc(20rem * var(--num-cards) + var(--feed-gap) * (var(--num-cards) - 1));
79
}
810
}
911
.cards {
10-
@screen mobileL {
12+
max-width: 100%;
13+
14+
@screen desktopL {
1115
max-width: calc(20rem * var(--num-cards) + var(--feed-gap) * (var(--num-cards) - 1));
1216
}
1317
}

packages/shared/src/components/Feed.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,9 @@ export default function Feed<T>({
179179
const currentSettings = useContext(FeedContext);
180180
const { user } = useContext(AuthContext);
181181
const { isFallback, query: routerQuery } = useRouter();
182-
const { openNewTab, spaciness, loadedSettings } = useContext(SettingsContext);
182+
const { openNewTab, loadedSettings } = useContext(SettingsContext);
183183
const { isListMode } = useFeedLayout();
184-
const numCards = currentSettings.numCards[spaciness ?? 'eco'];
184+
const numCards = currentSettings.numCards.eco;
185185
const isSquadFeed = feedName === OtherFeedPage.Squad;
186186
const { shouldUseListFeedLayout } = useFeedLayout();
187187
const trackedFeedFinish = useRef(false);

packages/shared/src/components/MainLayout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,14 @@ function MainLayoutComponent({
196196
/>
197197
<main
198198
className={classNames(
199-
'flex flex-col',
199+
'flex flex-col transition-[padding] duration-300 ease-in-out laptop:pt-16',
200200
showSidebar && 'tablet:pl-16 laptop:pl-11',
201201
className,
202202
isAuthReady &&
203203
!isScreenCentered &&
204204
sidebarExpanded &&
205205
'laptop:!pl-60',
206-
isBannerAvailable && 'laptop:pt-8',
206+
isBannerAvailable && 'laptop:pt-24',
207207
)}
208208
>
209209
{isAuthReady && showSidebar && (

packages/shared/src/components/feeds/FeedContainer.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ const cardListClass = {
6969
export const getFeedGapPx = {
7070
'gap-2': 8,
7171
'gap-3': 12,
72+
'gap-4': 16,
7273
'gap-5': 20,
74+
'gap-6': 24,
7375
'gap-8': 32,
7476
'gap-12': 48,
7577
'gap-14': 56,
@@ -87,7 +89,7 @@ export const gapClass = ({
8789
if (isFeedLayoutList) {
8890
return '';
8991
}
90-
return isList ? listGaps[space] ?? 'gap-2' : gridGaps[space] ?? 'gap-8';
92+
return isList ? listGaps[space] ?? 'gap-2' : gridGaps[space] ?? 'gap-4';
9193
};
9294

9395
const cardClass = ({
@@ -149,15 +151,15 @@ export const FeedContainer = ({
149151
}: FeedContainerProps): ReactElement => {
150152
const currentSettings = useContext(FeedContext);
151153
const { subject } = useToastNotification();
152-
const { spaciness, loadedSettings } = useContext(SettingsContext);
154+
const { loadedSettings } = useContext(SettingsContext);
153155
const { shouldUseListFeedLayout, isListMode } = useFeedLayout();
154156
const isLaptop = useViewSize(ViewSize.Laptop);
155157
const { feedName } = useActiveFeedNameContext();
156158
const { isAnyExplore, isExplorePopular, isExploreLatest } = useFeedName({
157159
feedName,
158160
});
159161
const router = useRouter();
160-
const numCards = currentSettings.numCards[spaciness ?? 'eco'];
162+
const numCards = currentSettings.numCards.eco;
161163
const isList =
162164
(isHorizontal || isListMode) && !shouldUseListFeedLayout
163165
? false
@@ -167,14 +169,14 @@ export const FeedContainer = ({
167169
gapClass({
168170
isList,
169171
isFeedLayoutList: shouldUseListFeedLayout,
170-
space: spaciness,
172+
space: 'eco',
171173
})
172174
];
173175
const style = {
174176
'--num-cards': isHorizontal && isListMode && numCards >= 2 ? 2 : numCards,
175177
'--feed-gap': `${feedGapPx / 16}rem`,
176178
} as CSSProperties;
177-
const cardContainerStyle = { ...getStyle(isList, spaciness) };
179+
const cardContainerStyle = { ...getStyle(isList, 'eco') };
178180
const isFinder = router.pathname === '/search/posts';
179181
const isSearch = showSearch && !isFinder;
180182

@@ -322,7 +324,7 @@ export const FeedContainer = ({
322324
gapClass({
323325
isList,
324326
isFeedLayoutList: shouldUseListFeedLayout,
325-
space: spaciness,
327+
space: 'eco',
326328
}),
327329
cardClass({ isList, numberOfCards: numCards, isHorizontal }),
328330
)}

packages/shared/src/components/feeds/FeedNav.tsx

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import classNames from 'classnames';
22
import type { ReactElement } from 'react';
3-
import React, { useMemo, useState, useTransition } from 'react';
3+
import React, { useMemo } from 'react';
44
import { useRouter } from 'next/router';
55
import { Tab, TabContainer } from '../tabs/TabContainer';
66
import { useActiveFeedNameContext } from '../../contexts';
77
import useActiveNav from '../../hooks/useActiveNav';
8-
import { useEventListener, useFeeds, useViewSize, ViewSize } from '../../hooks';
8+
import { useFeeds, useViewSize, ViewSize } from '../../hooks';
99
import usePersistentContext from '../../hooks/usePersistentContext';
1010
import {
1111
algorithmsList,
@@ -31,7 +31,6 @@ import { SharedFeedPage } from '../utilities';
3131
import PlusMobileEntryBanner from '../banners/PlusMobileEntryBanner';
3232
import { TargetType } from '../../lib/log';
3333
import usePlusEntry from '../../hooks/usePlusEntry';
34-
import { useAlertsContext } from '../../contexts/AlertContext';
3534

3635
enum FeedNavTab {
3736
ForYou = 'For you',
@@ -52,17 +51,12 @@ const StickyNavIconWrapper = classed(
5251
'sticky flex h-14 pt-1 -translate-y-16 items-center justify-end bg-gradient-to-r from-transparent via-background-default via-40% to-background-default pr-4',
5352
);
5453

55-
const MIN_SCROLL_BEFORE_HIDING = 60;
56-
5754
function FeedNav(): ReactElement {
5855
const router = useRouter();
59-
const [, startTransition] = useTransition();
60-
const [isHeaderVisible, setIsHeaderVisible] = useState(true);
6156
const { feedName } = useActiveFeedNameContext();
6257
const { sortingEnabled } = useSettingsContext();
6358
const { isSortableFeed } = useFeedName({ feedName });
6459
const { home, bookmarks } = useActiveNav(feedName);
65-
const { alerts } = useAlertsContext();
6660
const isMobile = useViewSize(ViewSize.MobileL);
6761
const [selectedAlgo, setSelectedAlgo] = usePersistentContext(
6862
DEFAULT_ALGORITHM_KEY,
@@ -82,8 +76,6 @@ function FeedNav(): ReactElement {
8276
isMobile &&
8377
((sortingEnabled && isSortableFeed) || feedName === SharedFeedPage.Custom);
8478

85-
const hasOpportunityAlert = !!alerts.opportunityId;
86-
8779
const urlToTab: Record<string, FeedNavTab> = useMemo(() => {
8880
const customFeeds = sortedFeeds.reduce((acc, { node: feed }) => {
8981
const isEditingFeed =
@@ -127,45 +119,16 @@ function FeedNav(): ReactElement {
127119
isCustomDefaultFeed,
128120
]);
129121

130-
const previousScrollY = React.useRef(0);
131-
132-
useEventListener(globalThis, 'scroll', () => {
133-
// when scrolled down we should hide the header
134-
// when scrolled up, we should bring it back
135-
const { scrollY } = window;
136-
const shouldHeaderBeVisible = scrollY < previousScrollY.current;
137-
138-
previousScrollY.current = scrollY;
139-
140-
if (shouldHeaderBeVisible === isHeaderVisible) {
141-
return;
142-
}
143-
144-
if (!shouldHeaderBeVisible && scrollY < MIN_SCROLL_BEFORE_HIDING) {
145-
return;
146-
}
147-
148-
startTransition(() => {
149-
setIsHeaderVisible(shouldHeaderBeVisible);
150-
});
151-
});
152122
const shouldRenderNav = home || (isMobile && bookmarks);
153123
if (!shouldRenderNav || router?.pathname?.startsWith('/posts/[id]')) {
154124
return null;
155125
}
156126

157-
const headerTransitionClasses =
158-
isMobile && hasOpportunityAlert
159-
? '-translate-y-[7.5rem] duration-[800ms]'
160-
: '-translate-y-26 duration-[800ms]';
161-
162127
return (
163128
<div
164129
className={classNames(
165-
'sticky top-0 z-header w-full transition-transform tablet:pl-16',
130+
'sticky top-0 z-header w-full bg-background-default tablet:pl-16',
166131
scrollClassName,
167-
isHeaderVisible && 'translate-y-0 duration-200',
168-
!isHeaderVisible && headerTransitionClasses,
169132
)}
170133
>
171134
{isMobile && <MobileFeedActions />}

packages/shared/src/components/layout/MainLayoutHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ function MainLayoutHeader({
9393
return (
9494
<header
9595
className={classNames(
96-
'sticky top-0 z-header h-14 flex-row content-center items-center justify-center gap-3 border-b border-border-subtlest-tertiary px-4 py-3 tablet:px-8 laptop:left-0 laptop:h-16 laptop:w-full laptop:px-4',
96+
'fixed top-0 z-header h-14 flex-row content-center items-center justify-center gap-3 border-b border-border-subtlest-tertiary bg-background-default px-4 py-3 tablet:px-8 laptop:left-0 laptop:h-16 laptop:w-full laptop:px-4',
9797
isMobileProfile ? 'hidden laptop:flex' : 'flex',
9898
hasBanner && 'laptop:top-8',
9999
isSearchPage && 'mb-16 laptop:mb-0',

packages/shared/src/components/layout/PageWrapperLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { ComponentProps, PropsWithChildren, ReactElement } from 'react';
22
import React from 'react';
33
import classNames from 'classnames';
44

5-
export const pageMainClassNames = 'tablet:p-4 laptop:px-10 laptop:py-5';
5+
export const pageMainClassNames = 'tablet:p-4 laptop:p-10';
66

77
export const PageWrapperLayout = ({
88
children,

packages/shared/src/components/search/SearchResults/SearchResultsLayout.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { PropsWithChildren, ReactElement } from 'react';
2-
import React, { useContext } from 'react';
2+
import React from 'react';
33
import { useRouter } from 'next/router';
44
import classNames from 'classnames';
55
import { PageWidgets } from '../../utilities';
@@ -12,7 +12,6 @@ import { webappUrl } from '../../../lib/constants';
1212
import { SearchResultsTags } from './SearchResultsTags';
1313
import { SearchResultsSources } from './SearchResultsSources';
1414
import { useSearchProviderSuggestions } from '../../../hooks/search';
15-
import SettingsContext from '../../../contexts/SettingsContext';
1615
import { gapClass } from '../../feeds/FeedContainer';
1716
import { useFeedLayout } from '../../../hooks';
1817
import { SearchResultsUsers } from './SearchResultsUsers';
@@ -26,7 +25,6 @@ export const SearchResultsLayout = (
2625
): ReactElement => {
2726
const { children } = props;
2827
const { isListMode } = useFeedLayout();
29-
const { spaciness } = useContext(SettingsContext);
3028
const { isSearchPageLaptop } = useSearchResultsLayout();
3129

3230
const {
@@ -103,7 +101,7 @@ export const SearchResultsLayout = (
103101
gapClass({
104102
isList: true,
105103
isFeedLayoutList: false,
106-
space: spaciness,
104+
space: 'eco',
107105
}),
108106
isListMode
109107
? `flex flex-col`

packages/shared/src/components/sidebar/Section.tsx

Lines changed: 75 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import React, { useRef } from 'react';
44
import type { ItemInnerProps, SidebarMenuItem } from './common';
55
import { NavHeader, NavSection } from './common';
66
import { SidebarItem } from './SidebarItem';
7-
import { Button, ButtonSize, ButtonVariant } from '../buttons/Button';
8-
import { ArrowIcon } from '../icons';
7+
import { ArrowIcon, PlusIcon } from '../icons';
98
import type { SettingsFlags } from '../../graphql/settings';
109
import { useSettingsContext } from '../../contexts/SettingsContext';
1110
import { isNullOrUndefined } from '../../lib/func';
1211
import useSidebarRendered from '../../hooks/useSidebarRendered';
12+
import Link from '../utilities/Link';
1313

1414
export interface SectionCommonProps
1515
extends Pick<ItemInnerProps, 'shouldShowLabel'> {
@@ -24,6 +24,8 @@ interface SectionProps extends SectionCommonProps {
2424
items: SidebarMenuItem[];
2525
isItemsButton: boolean;
2626
isAlwaysOpenOnMobile?: boolean;
27+
onAdd?: () => void;
28+
addHref?: string;
2729
}
2830

2931
export function Section({
@@ -36,6 +38,8 @@ export function Section({
3638
className,
3739
flag,
3840
isAlwaysOpenOnMobile,
41+
onAdd,
42+
addHref,
3943
}: SectionProps): ReactElement {
4044
const { flags, updateFlag } = useSettingsContext();
4145
const { sidebarRendered } = useSidebarRendered();
@@ -49,30 +53,79 @@ export function Section({
4953
};
5054

5155
return (
52-
<NavSection className={className}>
56+
<NavSection className={classNames('mt-1', className)}>
5357
{title && (
54-
<NavHeader
55-
className={classNames(
56-
'hidden justify-between laptop:flex',
57-
sidebarExpanded ? 'px-3 opacity-100' : 'px-0 opacity-0',
58-
)}
59-
>
60-
{title}
61-
<Button
62-
variant={ButtonVariant.Tertiary}
63-
onClick={toggleFlag}
64-
size={ButtonSize.XSmall}
65-
aria-label={`Toggle ${title}`}
66-
icon={
58+
<NavHeader className="relative hidden laptop:flex">
59+
{/* Divider shown when sidebar is collapsed */}
60+
<div
61+
className={classNames(
62+
'absolute inset-x-0 flex items-center justify-center px-2 transition-opacity duration-300',
63+
sidebarExpanded ? 'opacity-0' : 'opacity-100',
64+
)}
65+
>
66+
<hr className="w-full border-t border-border-subtlest-tertiary" />
67+
</div>
68+
{/* Header content shown when sidebar is expanded */}
69+
<div
70+
className={classNames(
71+
'group/section flex min-h-9 w-full items-center justify-between px-2 py-1.5 transition-opacity duration-300',
72+
sidebarExpanded ? 'opacity-100' : 'pointer-events-none opacity-0',
73+
)}
74+
>
75+
<button
76+
type="button"
77+
onClick={toggleFlag}
78+
aria-label={`Toggle ${title}`}
79+
aria-expanded={!!isVisible.current}
80+
className="flex items-center gap-1 rounded-6 transition-colors hover:text-text-primary"
81+
>
82+
<span
83+
className={classNames(
84+
'text-text-quaternary typo-callout',
85+
!sidebarExpanded && 'opacity-0',
86+
)}
87+
>
88+
{title}
89+
</span>
6790
<ArrowIcon
68-
className={isVisible.current ? 'rotate-360' : 'rotate-180'}
91+
className={classNames(
92+
'h-2.5 w-2.5 text-text-quaternary transition-transform duration-200',
93+
isVisible.current ? 'rotate-180' : 'rotate-90',
94+
)}
6995
/>
70-
}
71-
/>
96+
</button>
97+
{addHref && (
98+
<Link href={addHref}>
99+
<a
100+
aria-label={`Add to ${title}`}
101+
className="flex h-6 w-6 items-center justify-center rounded-6 text-text-tertiary transition-all hover:bg-surface-hover hover:text-text-primary"
102+
>
103+
<PlusIcon className="h-4 w-4" />
104+
</a>
105+
</Link>
106+
)}
107+
{!addHref && onAdd && (
108+
<button
109+
type="button"
110+
onClick={onAdd}
111+
aria-label={`Add to ${title}`}
112+
className="flex h-6 w-6 items-center justify-center rounded-6 text-text-tertiary transition-all hover:bg-surface-hover hover:text-text-primary"
113+
>
114+
<PlusIcon className="h-4 w-4" />
115+
</button>
116+
)}
117+
</div>
72118
</NavHeader>
73119
)}
74-
{(isVisible.current || shouldAlwaysBeVisible) &&
75-
items.map((item) => (
120+
<div
121+
className={classNames(
122+
'flex flex-col overflow-hidden transition-all duration-300',
123+
isVisible.current || shouldAlwaysBeVisible
124+
? 'max-h-[2000px] opacity-100' // Using large max-height for CSS transition animation
125+
: 'max-h-0 opacity-0',
126+
)}
127+
>
128+
{items.map((item) => (
76129
<SidebarItem
77130
key={`${item.title}-${item.path}`}
78131
item={item}
@@ -81,6 +134,7 @@ export function Section({
81134
shouldShowLabel={shouldShowLabel}
82135
/>
83136
))}
137+
</div>
84138
</NavSection>
85139
);
86140
}

0 commit comments

Comments
 (0)