diff --git a/app/javascript/mastodon/features/navigation_panel/redesign/navigation_link.module.scss b/app/javascript/mastodon/features/navigation_panel/redesign/navigation_link.module.scss
index 3aec81ebb58..acf44c67ed4 100644
--- a/app/javascript/mastodon/features/navigation_panel/redesign/navigation_link.module.scss
+++ b/app/javascript/mastodon/features/navigation_panel/redesign/navigation_link.module.scss
@@ -61,6 +61,7 @@
}
.label {
+ max-width: 100%;
min-width: 0;
flex-grow: 1;
flex-shrink: 1;
diff --git a/app/javascript/mastodon/features/navigation_panel/redesign/styles.module.scss b/app/javascript/mastodon/features/navigation_panel/redesign/styles.module.scss
index 52463424f55..43a4a3ccd4d 100644
--- a/app/javascript/mastodon/features/navigation_panel/redesign/styles.module.scss
+++ b/app/javascript/mastodon/features/navigation_panel/redesign/styles.module.scss
@@ -5,6 +5,7 @@
flex-direction: column;
height: 100%;
overflow-y: auto;
+ scrollbar-width: thin;
}
.list {
diff --git a/app/javascript/mastodon/features/ui/components/columns_area.tsx b/app/javascript/mastodon/features/ui/components/columns_area.tsx
deleted file mode 100644
index 0a6eec74341..00000000000
--- a/app/javascript/mastodon/features/ui/components/columns_area.tsx
+++ /dev/null
@@ -1,213 +0,0 @@
-import {
- Children,
- cloneElement,
- forwardRef,
- isValidElement,
- lazy,
- Suspense,
- useCallback,
-} from 'react';
-
-import classNames from 'classnames';
-
-import type { List, Record } from 'immutable';
-
-import { ColumnIndexContext } from '@/mastodon/components/column/context';
-import { useAppSelector } from '@/mastodon/store';
-import { isRedesignEnabled } from '@/mastodon/utils/environment';
-import { Footer } from 'mastodon/features/custom_homepage/components/footer';
-import { Header } from 'mastodon/features/custom_homepage/components/header';
-import { CollapsibleNavigationPanel } from 'mastodon/features/navigation_panel';
-
-import { useBreakpoint } from '../hooks/useBreakpoint';
-import {
- Compose,
- Notifications,
- HomeTimeline,
- CommunityTimeline,
- PublicTimeline,
- HashtagTimeline,
- DirectTimeline,
- FavouritedStatuses,
- BookmarkedStatuses,
- ListTimeline,
- Directory,
-} from '../util/async-components';
-import { useColumnsContext } from '../util/columns_context';
-
-import Bundle from './bundle';
-import { BundleColumnError } from './bundle_column_error';
-import { ColumnLoading } from './column_loading';
-import { ComposePanel, RedirectToMobileComposeIfNeeded } from './compose_panel';
-import DrawerLoading from './drawer_loading';
-
-const LazyRedesignNavigationPanel = lazy(() =>
- import('@/mastodon/features/navigation_panel/redesign').then(
- ({ RedesignNavigationPanel }) => ({ default: RedesignNavigationPanel }),
- ),
-);
-
-const componentMap = {
- COMPOSE: Compose,
- HOME: HomeTimeline,
- NOTIFICATIONS: Notifications,
- PUBLIC: PublicTimeline,
- REMOTE: PublicTimeline,
- COMMUNITY: CommunityTimeline,
- HASHTAG: HashtagTimeline,
- DIRECT: DirectTimeline,
- FAVOURITES: FavouritedStatuses,
- BOOKMARKS: BookmarkedStatuses,
- LIST: ListTimeline,
- DIRECTORY: Directory,
-} as const;
-
-const TabsBarPortal = () => {
- const { setTabsBarElement } = useColumnsContext();
-
- const setRef = useCallback(
- (element: HTMLDivElement | null) => {
- if (element) {
- setTabsBarElement(element);
- }
- },
- [setTabsBarElement],
- );
-
- return
;
-};
-
-interface Column {
- uuid: string;
- id: keyof typeof componentMap;
- params?: null | Record<{ other?: unknown }>;
-}
-
-type FetchedComponent = React.FC<{
- columnId?: string;
- multiColumn?: boolean;
- params: unknown;
-}>;
-
-export const ColumnsArea = forwardRef<
- HTMLDivElement,
- {
- singleColumn?: boolean;
- minimalShell?: boolean;
- children: React.ReactElement | React.ReactElement[];
- }
->(({ children, minimalShell, singleColumn }, ref) => {
- const renderComposePanel = !useBreakpoint('full');
- const columns = useAppSelector(
- (state) => state.settings.get('columns') as List>,
- );
- const isModalOpen = useAppSelector(
- (state) => !state.modal.get('stack').isEmpty(),
- );
-
- if (minimalShell) {
- return (
-
-
-
-
-
-
-
-
-
{children}
-
-
-
-
- );
- }
-
- if (singleColumn) {
- return (
-
-
-
- {isRedesignEnabled() ? (
-
-
-
- ) : (
- <>
- {renderComposePanel && }
-
- >
- )}
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
- );
- }
-
- return (
-
- {columns.map((column, index) => {
- const params = column.get('params')
- ? column.get('params')?.toJS()
- : null;
- const other = params?.other ?? {};
- const uuid = column.get('uuid');
- const id = column.get('id');
-
- return (
-
-
- {(SpecificComponent: FetchedComponent) => (
-
- )}
-
-
- );
- })}
-
-
- {Children.map(children, (child) =>
- isValidElement<{ multiColumn?: boolean }>(child)
- ? cloneElement(child, { multiColumn: true })
- : child,
- )}
-
-
- );
-});
-
-ColumnsArea.displayName = 'ColumnsArea';
-
-const ErrorComponent = (props: { onRetry: () => void }) => {
- return ;
-};
-
-const renderLoading = (columnId: string) => {
- const LoadingComponent =
- columnId === 'COMPOSE' ? : ;
- return () => LoadingComponent;
-};
diff --git a/app/javascript/mastodon/features/ui/components/columns_area/index.tsx b/app/javascript/mastodon/features/ui/components/columns_area/index.tsx
new file mode 100644
index 00000000000..c2dd3bfaa9d
--- /dev/null
+++ b/app/javascript/mastodon/features/ui/components/columns_area/index.tsx
@@ -0,0 +1,122 @@
+import { lazy, Suspense, useCallback } from 'react';
+
+import classNames from 'classnames';
+
+import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
+import { Footer } from '@/mastodon/features/custom_homepage/components/footer';
+import { Header } from '@/mastodon/features/custom_homepage/components/header';
+import { CollapsibleNavigationPanel } from '@/mastodon/features/navigation_panel';
+import { useBreakpoint } from '@/mastodon/features/ui/hooks/useBreakpoint';
+import { useColumnsContext } from '@/mastodon/features/ui/util/columns_context';
+import { useAppSelector } from '@/mastodon/store';
+import { isRedesignEnabled } from '@/mastodon/utils/environment';
+
+import {
+ ComposePanel,
+ RedirectToMobileComposeIfNeeded,
+} from '../compose_panel';
+
+import { MultiColumnContent } from './multi_column_content';
+
+const LazyColumnsAreaRedesign = lazy(() =>
+ import('@/mastodon/features/ui/components/columns_area/redesign').then(
+ ({ ColumnsAreaRedesign }) => ({ default: ColumnsAreaRedesign }),
+ ),
+);
+
+const TabsBarPortal = () => {
+ const { setTabsBarElement } = useColumnsContext();
+
+ const setRef = useCallback(
+ (element: HTMLDivElement | null) => {
+ if (element) {
+ setTabsBarElement(element);
+ }
+ },
+ [setTabsBarElement],
+ );
+
+ return ;
+};
+
+interface ColumnsAreaProps {
+ singleColumn?: boolean;
+ minimalShell?: boolean;
+ children: React.ReactElement | React.ReactElement[];
+ ref?: React.Ref;
+}
+
+const ColumnsAreaLegacy: React.FC = ({
+ children,
+ minimalShell,
+ singleColumn,
+ ref,
+}) => {
+ const renderComposePanel = !useBreakpoint('full');
+ const isModalOpen = useAppSelector(
+ (state) => !state.modal.get('stack').isEmpty(),
+ );
+
+ if (minimalShell) {
+ return (
+
+
+
+
+
+
+
+
+
{children}
+
+
+
+
+ );
+ }
+
+ if (singleColumn) {
+ return (
+
+
+
+ {renderComposePanel && }
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+ );
+ }
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const ColumnsArea: React.FC = (props) => {
+ if (isRedesignEnabled()) {
+ return (
+ }>
+
+
+ );
+ } else {
+ return ;
+ }
+};
diff --git a/app/javascript/mastodon/features/ui/components/columns_area/multi_column_content.tsx b/app/javascript/mastodon/features/ui/components/columns_area/multi_column_content.tsx
new file mode 100644
index 00000000000..957d9d13f3f
--- /dev/null
+++ b/app/javascript/mastodon/features/ui/components/columns_area/multi_column_content.tsx
@@ -0,0 +1,109 @@
+import { Children, cloneElement, isValidElement } from 'react';
+
+import type { List, Record } from 'immutable';
+
+import { ColumnIndexContext } from '@/mastodon/components/column/context';
+import { useAppSelector } from '@/mastodon/store';
+
+import {
+ Compose,
+ Notifications,
+ HomeTimeline,
+ CommunityTimeline,
+ PublicTimeline,
+ HashtagTimeline,
+ DirectTimeline,
+ FavouritedStatuses,
+ BookmarkedStatuses,
+ ListTimeline,
+ Directory,
+} from '../../util/async-components';
+import Bundle from '../bundle';
+import { BundleColumnError } from '../bundle_column_error';
+import { ColumnLoading } from '../column_loading';
+import DrawerLoading from '../drawer_loading';
+
+const componentMap = {
+ COMPOSE: Compose,
+ HOME: HomeTimeline,
+ NOTIFICATIONS: Notifications,
+ PUBLIC: PublicTimeline,
+ REMOTE: PublicTimeline,
+ COMMUNITY: CommunityTimeline,
+ HASHTAG: HashtagTimeline,
+ DIRECT: DirectTimeline,
+ FAVOURITES: FavouritedStatuses,
+ BOOKMARKS: BookmarkedStatuses,
+ LIST: ListTimeline,
+ DIRECTORY: Directory,
+} as const;
+
+interface Column {
+ uuid: string;
+ id: keyof typeof componentMap;
+ params?: null | Record<{ other?: unknown }>;
+}
+
+type FetchedComponent = React.FC<{
+ columnId?: string;
+ multiColumn?: boolean;
+ params: unknown;
+}>;
+
+const ErrorComponent = (props: { onRetry: () => void }) => (
+
+);
+
+const renderLoading = (columnId: string) => {
+ const LoadingComponent =
+ columnId === 'COMPOSE' ? : ;
+ return () => LoadingComponent;
+};
+
+export const MultiColumnContent: React.FC<{
+ children: React.ReactElement | React.ReactElement[];
+}> = ({ children }) => {
+ const columns = useAppSelector(
+ (state) => state.settings.get('columns') as List>,
+ );
+ return (
+ <>
+ {columns.map((column, index) => {
+ const params = column.get('params')
+ ? column.get('params')?.toJS()
+ : null;
+ const other = params?.other ?? {};
+ const uuid = column.get('uuid');
+ const id = column.get('id');
+
+ return (
+
+
+ {(SpecificComponent: FetchedComponent) => (
+
+ )}
+
+
+ );
+ })}
+
+
+ {Children.map(children, (child) =>
+ isValidElement<{ multiColumn?: boolean }>(child)
+ ? cloneElement(child, { multiColumn: true })
+ : child,
+ )}
+
+ >
+ );
+};
diff --git a/app/javascript/mastodon/features/ui/components/columns_area/redesign.module.scss b/app/javascript/mastodon/features/ui/components/columns_area/redesign.module.scss
new file mode 100644
index 00000000000..73c6078a0b1
--- /dev/null
+++ b/app/javascript/mastodon/features/ui/components/columns_area/redesign.module.scss
@@ -0,0 +1,56 @@
+@use '@/styles/mastodon/variables' as *;
+
+.root {
+ --main-column-max-width: 640px;
+ --navigation-min-width: 280px;
+ --navigation-max-width: 360px;
+
+ box-sizing: border-box;
+ width: 100%;
+ height: 100%;
+ min-height: 100vh;
+ min-height: 100dvh;
+ padding-bottom: env(safe-area-inset-bottom);
+
+ @media (min-width: $no-gap-breakpoint) {
+ padding-inline: var(--space-2xs);
+ }
+
+ @media (min-width: $mobile-menu-breakpoint) {
+ display: grid;
+ grid-template-columns:
+ minmax(var(--navigation-min-width), 1fr)
+ minmax(0, var(--main-column-max-width))
+ minmax(0, 1fr);
+ gap: var(--space-xs);
+ }
+}
+
+.navigationWrapper {
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ max-width: var(--navigation-max-width);
+ height: 100dvh;
+ padding-bottom: env(safe-area-inset-bottom);
+
+ @media (max-width: ($mobile-menu-breakpoint - 1)) {
+ display: none;
+ }
+}
+
+.main {
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ flex: 0 1 auto;
+ width: 100%;
+ contain: inline-size layout paint style;
+ container: column / inline-size;
+ color: var(--color-text-primary);
+ background-color: var(--color-bg-primary);
+
+ @media ($mobile-menu-breakpoint < width < $no-gap-breakpoint) {
+ border-inline: 1px solid var(--color-border-primary);
+ }
+}
diff --git a/app/javascript/mastodon/features/ui/components/columns_area/redesign.tsx b/app/javascript/mastodon/features/ui/components/columns_area/redesign.tsx
new file mode 100644
index 00000000000..00a32c7e099
--- /dev/null
+++ b/app/javascript/mastodon/features/ui/components/columns_area/redesign.tsx
@@ -0,0 +1,90 @@
+import { useCallback } from 'react';
+
+import classNames from 'classnames';
+
+import { CollapsibleNavigationPanel } from '@/mastodon/features/navigation_panel';
+import { RedesignNavigationPanel } from '@/mastodon/features/navigation_panel/redesign';
+import { useAppSelector } from '@/mastodon/store';
+import { Footer } from 'mastodon/features/custom_homepage/components/footer';
+import { Header } from 'mastodon/features/custom_homepage/components/header';
+
+import { useBreakpoint } from '../../hooks/useBreakpoint';
+import { useColumnsContext } from '../../util/columns_context';
+
+import { MultiColumnContent } from './multi_column_content';
+import classes from './redesign.module.scss';
+
+const TabsBarPortal = () => {
+ const { setTabsBarElement } = useColumnsContext();
+
+ const setRef = useCallback(
+ (element: HTMLDivElement | null) => {
+ if (element) {
+ setTabsBarElement(element);
+ }
+ },
+ [setTabsBarElement],
+ );
+
+ return ;
+};
+
+export const ColumnsAreaRedesign: React.FC<{
+ singleColumn?: boolean;
+ minimalShell?: boolean;
+ children: React.ReactElement | React.ReactElement[];
+ ref?: React.Ref;
+}> = ({ children, minimalShell, singleColumn, ref }) => {
+ const isModalOpen = useAppSelector(
+ (state) => !state.modal.get('stack').isEmpty(),
+ );
+ const renderLegacyNavForMobile = useBreakpoint('openable');
+
+ if (minimalShell) {
+ return (
+
+
+
+
+
+
+
+
+
{children}
+
+
+
+
+ );
+ }
+
+ if (singleColumn) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+ {renderLegacyNavForMobile && }
+
+
+ );
+ }
+
+ return (
+
+ {children}
+
+ );
+};