Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/shared/src/components/Feed.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
grid-template-columns: 100%;
}
.container {
@screen laptopL {
max-width: 100%;

@screen desktopL {
max-width: calc(20rem * var(--num-cards) + var(--feed-gap) * (var(--num-cards) - 1));
}
}
.cards {
@screen mobileL {
max-width: 100%;

@screen desktopL {
max-width: calc(20rem * var(--num-cards) + var(--feed-gap) * (var(--num-cards) - 1));
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/src/components/Feed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ export default function Feed<T>({
const currentSettings = useContext(FeedContext);
const { user } = useContext(AuthContext);
const { isFallback, query: routerQuery } = useRouter();
const { openNewTab, spaciness, loadedSettings } = useContext(SettingsContext);
const { openNewTab, loadedSettings } = useContext(SettingsContext);
const { isListMode } = useFeedLayout();
const numCards = currentSettings.numCards[spaciness ?? 'eco'];
const numCards = currentSettings.numCards.eco;
const isSquadFeed = feedName === OtherFeedPage.Squad;
const { shouldUseListFeedLayout } = useFeedLayout();
const trackedFeedFinish = useRef(false);
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/src/components/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,14 @@ function MainLayoutComponent({
/>
<main
className={classNames(
'flex flex-col',
'flex flex-col pt-14 transition-[padding] duration-300 ease-in-out laptop:pt-16',
showSidebar && 'tablet:pl-16 laptop:pl-11',
className,
isAuthReady &&
!isScreenCentered &&
sidebarExpanded &&
'laptop:!pl-60',
isBannerAvailable && 'laptop:pt-8',
isBannerAvailable && 'laptop:pt-24',
)}
>
{isAuthReady && showSidebar && (
Expand Down
14 changes: 8 additions & 6 deletions packages/shared/src/components/feeds/FeedContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ const cardListClass = {
export const getFeedGapPx = {
'gap-2': 8,
'gap-3': 12,
'gap-4': 16,
'gap-5': 20,
'gap-6': 24,
'gap-8': 32,
'gap-12': 48,
'gap-14': 56,
Expand All @@ -87,7 +89,7 @@ export const gapClass = ({
if (isFeedLayoutList) {
return '';
}
return isList ? listGaps[space] ?? 'gap-2' : gridGaps[space] ?? 'gap-8';
return isList ? listGaps[space] ?? 'gap-2' : gridGaps[space] ?? 'gap-4';
};

const cardClass = ({
Expand Down Expand Up @@ -149,15 +151,15 @@ export const FeedContainer = ({
}: FeedContainerProps): ReactElement => {
const currentSettings = useContext(FeedContext);
const { subject } = useToastNotification();
const { spaciness, loadedSettings } = useContext(SettingsContext);
const { loadedSettings } = useContext(SettingsContext);
const { shouldUseListFeedLayout, isListMode } = useFeedLayout();
const isLaptop = useViewSize(ViewSize.Laptop);
const { feedName } = useActiveFeedNameContext();
const { isAnyExplore, isExplorePopular, isExploreLatest } = useFeedName({
feedName,
});
const router = useRouter();
const numCards = currentSettings.numCards[spaciness ?? 'eco'];
const numCards = currentSettings.numCards.eco;
const isList =
(isHorizontal || isListMode) && !shouldUseListFeedLayout
? false
Expand All @@ -167,14 +169,14 @@ export const FeedContainer = ({
gapClass({
isList,
isFeedLayoutList: shouldUseListFeedLayout,
space: spaciness,
space: 'eco',
})
];
const style = {
'--num-cards': isHorizontal && isListMode && numCards >= 2 ? 2 : numCards,
'--feed-gap': `${feedGapPx / 16}rem`,
} as CSSProperties;
const cardContainerStyle = { ...getStyle(isList, spaciness) };
const cardContainerStyle = { ...getStyle(isList, 'eco') };
const isFinder = router.pathname === '/search/posts';
const isSearch = showSearch && !isFinder;

Expand Down Expand Up @@ -322,7 +324,7 @@ export const FeedContainer = ({
gapClass({
isList,
isFeedLayoutList: shouldUseListFeedLayout,
space: spaciness,
space: 'eco',
}),
cardClass({ isList, numberOfCards: numCards, isHorizontal }),
)}
Expand Down
43 changes: 3 additions & 40 deletions packages/shared/src/components/feeds/FeedNav.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import classNames from 'classnames';
import type { ReactElement } from 'react';
import React, { useMemo, useState, useTransition } from 'react';
import React, { useMemo } from 'react';
import { useRouter } from 'next/router';
import { Tab, TabContainer } from '../tabs/TabContainer';
import { useActiveFeedNameContext } from '../../contexts';
import useActiveNav from '../../hooks/useActiveNav';
import { useEventListener, useFeeds, useViewSize, ViewSize } from '../../hooks';
import { useFeeds, useViewSize, ViewSize } from '../../hooks';
import usePersistentContext from '../../hooks/usePersistentContext';
import {
algorithmsList,
Expand All @@ -31,7 +31,6 @@ import { SharedFeedPage } from '../utilities';
import PlusMobileEntryBanner from '../banners/PlusMobileEntryBanner';
import { TargetType } from '../../lib/log';
import usePlusEntry from '../../hooks/usePlusEntry';
import { useAlertsContext } from '../../contexts/AlertContext';

enum FeedNavTab {
ForYou = 'For you',
Expand All @@ -52,17 +51,12 @@ const StickyNavIconWrapper = classed(
'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',
);

const MIN_SCROLL_BEFORE_HIDING = 60;

function FeedNav(): ReactElement {
const router = useRouter();
const [, startTransition] = useTransition();
const [isHeaderVisible, setIsHeaderVisible] = useState(true);
const { feedName } = useActiveFeedNameContext();
const { sortingEnabled } = useSettingsContext();
const { isSortableFeed } = useFeedName({ feedName });
const { home, bookmarks } = useActiveNav(feedName);
const { alerts } = useAlertsContext();
const isMobile = useViewSize(ViewSize.MobileL);
const [selectedAlgo, setSelectedAlgo] = usePersistentContext(
DEFAULT_ALGORITHM_KEY,
Expand All @@ -82,8 +76,6 @@ function FeedNav(): ReactElement {
isMobile &&
((sortingEnabled && isSortableFeed) || feedName === SharedFeedPage.Custom);

const hasOpportunityAlert = !!alerts.opportunityId;

const urlToTab: Record<string, FeedNavTab> = useMemo(() => {
const customFeeds = sortedFeeds.reduce((acc, { node: feed }) => {
const isEditingFeed =
Expand Down Expand Up @@ -127,45 +119,16 @@ function FeedNav(): ReactElement {
isCustomDefaultFeed,
]);

const previousScrollY = React.useRef(0);

useEventListener(globalThis, 'scroll', () => {
// when scrolled down we should hide the header
// when scrolled up, we should bring it back
const { scrollY } = window;
const shouldHeaderBeVisible = scrollY < previousScrollY.current;

previousScrollY.current = scrollY;

if (shouldHeaderBeVisible === isHeaderVisible) {
return;
}

if (!shouldHeaderBeVisible && scrollY < MIN_SCROLL_BEFORE_HIDING) {
return;
}

startTransition(() => {
setIsHeaderVisible(shouldHeaderBeVisible);
});
});
const shouldRenderNav = home || (isMobile && bookmarks);
if (!shouldRenderNav || router?.pathname?.startsWith('/posts/[id]')) {
return null;
}

const headerTransitionClasses =
isMobile && hasOpportunityAlert
? '-translate-y-[7.5rem] duration-[800ms]'
: '-translate-y-26 duration-[800ms]';

return (
<div
className={classNames(
'sticky top-0 z-header w-full transition-transform tablet:pl-16',
'fixed top-0 z-header w-full bg-background-default tablet:pl-16',
scrollClassName,
isHeaderVisible && 'translate-y-0 duration-200',
!isHeaderVisible && headerTransitionClasses,
)}
>
{isMobile && <MobileFeedActions />}
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/src/components/layout/MainLayoutHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ function MainLayoutHeader({
if (loadedSettings && !isLaptop) {
if (isSearchPage) {
return (
<div className="sticky top-0 z-header w-full bg-background-default tablet:pl-16">
<div className="fixed top-0 z-header w-full bg-background-default tablet:pl-16">
<RenderSearchPanel />
{!isSearch && <MobileExploreHeader path={feedName as string} />}
</div>
Expand All @@ -93,7 +93,7 @@ function MainLayoutHeader({
return (
<header
className={classNames(
'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',
'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',
isMobileProfile ? 'hidden laptop:flex' : 'flex',
hasBanner && 'laptop:top-8',
isSearchPage && 'mb-16 laptop:mb-0',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ComponentProps, PropsWithChildren, ReactElement } from 'react';
import React from 'react';
import classNames from 'classnames';

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

export const PageWrapperLayout = ({
children,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { PropsWithChildren, ReactElement } from 'react';
import React, { useContext } from 'react';
import React from 'react';
import { useRouter } from 'next/router';
import classNames from 'classnames';
import { PageWidgets } from '../../utilities';
Expand All @@ -12,7 +12,6 @@ import { webappUrl } from '../../../lib/constants';
import { SearchResultsTags } from './SearchResultsTags';
import { SearchResultsSources } from './SearchResultsSources';
import { useSearchProviderSuggestions } from '../../../hooks/search';
import SettingsContext from '../../../contexts/SettingsContext';
import { gapClass } from '../../feeds/FeedContainer';
import { useFeedLayout } from '../../../hooks';
import { SearchResultsUsers } from './SearchResultsUsers';
Expand All @@ -26,7 +25,6 @@ export const SearchResultsLayout = (
): ReactElement => {
const { children } = props;
const { isListMode } = useFeedLayout();
const { spaciness } = useContext(SettingsContext);
const { isSearchPageLaptop } = useSearchResultsLayout();

const {
Expand Down Expand Up @@ -103,7 +101,7 @@ export const SearchResultsLayout = (
gapClass({
isList: true,
isFeedLayoutList: false,
space: spaciness,
space: 'eco',
}),
isListMode
? `flex flex-col`
Expand Down
90 changes: 68 additions & 22 deletions packages/shared/src/components/sidebar/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import React, { useRef } from 'react';
import type { ItemInnerProps, SidebarMenuItem } from './common';
import { NavHeader, NavSection } from './common';
import { SidebarItem } from './SidebarItem';
import { Button, ButtonSize, ButtonVariant } from '../buttons/Button';
import { ArrowIcon } from '../icons';
import { ArrowIcon, PlusIcon } from '../icons';
import type { SettingsFlags } from '../../graphql/settings';
import { useSettingsContext } from '../../contexts/SettingsContext';
import { isNullOrUndefined } from '../../lib/func';
import useSidebarRendered from '../../hooks/useSidebarRendered';
import Link from '../utilities/Link';

export interface SectionCommonProps
extends Pick<ItemInnerProps, 'shouldShowLabel'> {
Expand All @@ -24,6 +24,8 @@ interface SectionProps extends SectionCommonProps {
items: SidebarMenuItem[];
isItemsButton: boolean;
isAlwaysOpenOnMobile?: boolean;
onAdd?: () => void;
addHref?: string;
}

export function Section({
Expand All @@ -36,6 +38,8 @@ export function Section({
className,
flag,
isAlwaysOpenOnMobile,
onAdd,
addHref,
}: SectionProps): ReactElement {
const { flags, updateFlag } = useSettingsContext();
const { sidebarRendered } = useSidebarRendered();
Expand All @@ -48,31 +52,72 @@ export function Section({
isVisible.current = !isVisible.current;
};

return (
<NavSection className={className}>
{title && (
<NavHeader
const headerContent = (
<div
className={classNames(
'group/section flex w-full items-center justify-between px-2 py-1.5 transition-opacity duration-300',
sidebarExpanded ? 'opacity-100' : 'pointer-events-none opacity-0',
)}
>
<button
type="button"
onClick={toggleFlag}
aria-label={`Toggle ${title}`}
aria-expanded={!!isVisible.current}
className="flex items-center gap-1 rounded-6 transition-colors hover:text-text-primary"
>
<span
className={classNames(
'hidden justify-between laptop:flex',
sidebarExpanded ? 'px-3 opacity-100' : 'px-0 opacity-0',
'text-text-quaternary typo-callout',
!sidebarExpanded && 'opacity-0',
)}
>
{title}
<Button
variant={ButtonVariant.Tertiary}
onClick={toggleFlag}
size={ButtonSize.XSmall}
aria-label={`Toggle ${title}`}
icon={
<ArrowIcon
className={isVisible.current ? 'rotate-360' : 'rotate-180'}
/>
}
/>
</NavHeader>
</span>
<ArrowIcon
className={classNames(
'h-2.5 w-2.5 text-text-quaternary transition-transform duration-200',
isVisible.current ? 'rotate-180' : 'rotate-90',
)}
/>
</button>
{addHref && (
<Link href={addHref}>
<a
aria-label={`Add to ${title}`}
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"
>
<PlusIcon className="h-4 w-4" />
</a>
</Link>
)}
{!addHref && onAdd && (
<button
type="button"
onClick={onAdd}
aria-label={`Add to ${title}`}
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"
>
<PlusIcon className="h-4 w-4" />
</button>
)}
</div>
);

return (
<NavSection className={classNames('mt-1', className)}>
{title && (
<NavHeader className="hidden laptop:flex">{headerContent}</NavHeader>
)}
{(isVisible.current || shouldAlwaysBeVisible) &&
items.map((item) => (
<div
className={classNames(
'flex flex-col overflow-hidden transition-all duration-200',
isVisible.current || shouldAlwaysBeVisible
? 'max-h-[2000px] opacity-100'
: 'max-h-0 opacity-0',
)}
>
{items.map((item) => (
<SidebarItem
key={`${item.title}-${item.path}`}
item={item}
Expand All @@ -81,6 +126,7 @@ export function Section({
shouldShowLabel={shouldShowLabel}
/>
))}
</div>
</NavSection>
);
}
Loading