fix styling

This commit is contained in:
plebeius
2026-02-24 16:36:13 +08:00
parent e51f5b968c
commit 0e1421fff7
9 changed files with 162 additions and 30 deletions
@@ -123,7 +123,7 @@ const VoteButton = () => {
); );
}; };
const RefreshButton = () => { export const RefreshButton = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const reset = useFeedResetStore((state) => state.reset); const reset = useFeedResetStore((state) => state.reset);
return ( return (
+1 -1
View File
@@ -1 +1 @@
export { MobileBoardButtons, DesktopBoardButtons, PostPageStats, ReturnButton, CatalogButton, UpdateButton, AutoButton, TopButton } from './board-buttons'; export { MobileBoardButtons, DesktopBoardButtons, PostPageStats, ReturnButton, CatalogButton, UpdateButton, AutoButton, TopButton, RefreshButton } from './board-buttons';
@@ -3,7 +3,15 @@
} }
.clickableErrorMessage { .clickableErrorMessage {
all: unset;
font: inherit;
color: red;
cursor: pointer; cursor: pointer;
display: inline;
}
.clickableErrorMessage:hover {
text-decoration: underline;
} }
.feedbackSuccessMessage { .feedbackSuccessMessage {
+60 -5
View File
@@ -10,21 +10,43 @@
justify-content: space-between; justify-content: space-between;
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 8px;
padding: 8px 0;
text-align: center; text-align: center;
} }
.styleRow {
padding: 8px 0 0;
display: flex;
justify-content: flex-end;
}
.styleRowContent {
display: flex;
align-items: center;
gap: 6px;
}
.boardsBarRow { .boardsBarRow {
padding: 8px 0; padding: 8px 0;
} }
.legalMeta { .legalMeta {
padding: 12px 0 0; padding: 12px 0 0;
font-size: 11px; font-size: 10px;
color: var(--body-font-color); color: var(--body-font-color);
text-align: center; text-align: center;
} }
.legalMeta a {
color: var(--boardsbar-desktop-link-text-color);
text-decoration: var(--boardsbar-desktop-link-text-decoration);
}
.legalMeta a:hover {
color: var(--boardsbar-desktop-link-text-color-hover);
text-decoration: var(--boardsbar-desktop-link-text-decoration-hover);
cursor: pointer;
}
/* Shared footer first-row layout (catalog, board pagination footer mode) */ /* Shared footer first-row layout (catalog, board pagination footer mode) */
.footerRow { .footerRow {
display: flex; display: flex;
@@ -55,10 +77,9 @@
/* Thread footer first row */ /* Thread footer first row */
.threadRow { .threadRow {
display: flex; display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center; align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px; gap: 12px;
width: 100%; width: 100%;
text-transform: capitalize; text-transform: capitalize;
@@ -69,22 +90,56 @@
align-items: center; align-items: center;
gap: 4px; gap: 4px;
flex-wrap: wrap; flex-wrap: wrap;
justify-self: start;
} }
.threadCenter { .threadCenter {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
justify-self: center;
} }
.threadRight { .threadRight {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px;
justify-self: end;
} }
/* Buttons and links in footer first row (catalog, thread): same text-link styling */
.threadRow button,
.threadRow .button, .threadRow .button,
.threadLeft a,
.footerRow button,
.footerRow .button,
.footerLeft a,
.button { .button {
all: unset;
font: inherit;
text-transform: capitalize; text-transform: capitalize;
color: var(--button-desktop-text-color);
text-decoration: var(--button-text-decoration);
cursor: pointer;
}
.threadRow button:hover:not(:disabled),
.threadRow .button:hover:not(:disabled),
.threadLeft a:hover,
.footerRow button:hover:not(:disabled),
.footerRow .button:hover:not(:disabled),
.footerLeft a:hover,
.button:hover:not(:disabled) {
color: var(--button-desktop-text-color-hover);
}
.threadRow button:disabled,
.threadRow .button:disabled,
.footerRow button:disabled,
.footerRow .button:disabled,
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
} }
@media (max-width: 640px) { @media (max-width: 640px) {
+66 -13
View File
@@ -3,7 +3,7 @@ import { useLocation, useParams } from 'react-router-dom';
import BoardsBar from '../boardsbar'; import BoardsBar from '../boardsbar';
import SiteLegalMeta from '../site-legal-meta'; import SiteLegalMeta from '../site-legal-meta';
import StyleSelector from '../style-selector/style-selector'; import StyleSelector from '../style-selector/style-selector';
import { ReturnButton, CatalogButton, TopButton, UpdateButton, AutoButton, PostPageStats } from '../board-buttons/board-buttons'; import { ReturnButton, CatalogButton, TopButton, UpdateButton, AutoButton, PostPageStats, RefreshButton } from '../board-buttons/board-buttons';
import { isAllView, isSubscriptionsView, isModView } from '../../lib/utils/view-utils'; import { isAllView, isSubscriptionsView, isModView } from '../../lib/utils/view-utils';
import useReplyModalStore from '../../stores/use-reply-modal-store'; import useReplyModalStore from '../../stores/use-reply-modal-store';
import styles from './footer.module.css'; import styles from './footer.module.css';
@@ -16,12 +16,16 @@ import styles from './footer.module.css';
export interface PageFooterDesktopProps { export interface PageFooterDesktopProps {
/** Mode-specific first row content (e.g. board pagination or thread controls) */ /** Mode-specific first row content (e.g. board pagination or thread controls) */
firstRow: React.ReactNode; firstRow: React.ReactNode;
/** Optional row between first row and BoardsBar (e.g. style selector on thread page) */
styleRow?: React.ReactNode;
} }
export const PageFooterDesktop = ({ firstRow }: PageFooterDesktopProps) => ( export const PageFooterDesktop = ({ firstRow, styleRow }: PageFooterDesktopProps) => (
<footer className={styles.footer}> <footer className={styles.footer}>
<hr /> <hr />
<div className={styles.firstRow}>{firstRow}</div> <div className={styles.firstRow}>{firstRow}</div>
<hr />
{styleRow != null ? <div className={styles.styleRow}>{styleRow}</div> : null}
<div className={styles.boardsBarRow}> <div className={styles.boardsBarRow}>
<BoardsBar /> <BoardsBar />
</div> </div>
@@ -33,14 +37,34 @@ export const PageFooterDesktop = ({ firstRow }: PageFooterDesktopProps) => (
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* CatalogFooterFirstRow * CatalogFooterFirstRow
* Catalog footer first row: Style selector on right only (no pagination). * Catalog footer first row: Return, Archive, Top, Refresh on left; Style selector on right.
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
export const CatalogFooterFirstRow = () => { export interface CatalogFooterFirstRowProps {
subplebbitAddress?: string;
isInAllView?: boolean;
isInSubscriptionsView?: boolean;
isInModView?: boolean;
}
export const CatalogFooterFirstRow = ({ subplebbitAddress, isInAllView = false, isInSubscriptionsView = false, isInModView = false }: CatalogFooterFirstRowProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div className={styles.footerRow}> <div className={styles.footerRow}>
<div className={styles.footerLeft} /> <div className={styles.footerLeft}>
<span>
[<ReturnButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
</span>
<span>
[<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
</span>
<span>
[<TopButton />]
</span>
<span>
[<RefreshButton />]
</span>
</div>
<div className={styles.footerRight}> <div className={styles.footerRight}>
<span className={styles.styleLabel}>{t('style')}:</span> <span className={styles.styleLabel}>{t('style')}:</span>
<StyleSelector /> <StyleSelector />
@@ -49,6 +73,21 @@ export const CatalogFooterFirstRow = () => {
); );
}; };
/* -----------------------------------------------------------------------------
* ThreadFooterStyleRow
* Style selector on its own row (thread page only, below first row, above BoardsBar).
* -------------------------------------------------------------------------- */
export const ThreadFooterStyleRow = () => {
const { t } = useTranslation();
return (
<span className={styles.styleRowContent}>
<span className={styles.styleLabel}>{t('style')}:</span>
<StyleSelector />
</span>
);
};
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* ThreadFooterFirstRow * ThreadFooterFirstRow
* Thread page footer first row: Return, Catalog, Top, Update, Auto, Post a Reply, stats. * Thread page footer first row: Return, Catalog, Top, Update, Auto, Post a Reply, stats.
@@ -80,16 +119,30 @@ export const ThreadFooterFirstRow = ({ postCid, threadNumber, subplebbitAddress,
return ( return (
<div className={styles.threadRow}> <div className={styles.threadRow}>
<div className={styles.threadLeft}> <div className={styles.threadLeft}>
[<ReturnButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />] [ <span>
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />] [ [<ReturnButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
<TopButton />] [<UpdateButton />] [<AutoButton />] </span>
<span>
[<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />]
</span>
<span>
[<TopButton />]
</span>
<span>
[<UpdateButton />]
</span>
<span>
[<AutoButton />]
</span>
</div> </div>
<div className={styles.threadCenter}> <div className={styles.threadCenter}>
[ <span>
<button type='button' className={styles.button} onClick={handlePostReplyClick} disabled={isThreadClosed} aria-label={t('post_a_reply')}> [
{t('post_a_reply')} <button type='button' className={styles.button} onClick={handlePostReplyClick} disabled={isThreadClosed} aria-label={t('post_a_reply')}>
</button> {t('post_a_reply')}
] </button>
]
</span>
</div> </div>
<div className={styles.threadRight}> <div className={styles.threadRight}>
<PostPageStats /> <PostPageStats />
+2 -2
View File
@@ -1,2 +1,2 @@
export { PageFooterDesktop, CatalogFooterFirstRow, ThreadFooterFirstRow } from './footer'; export { PageFooterDesktop, CatalogFooterFirstRow, ThreadFooterFirstRow, ThreadFooterStyleRow } from './footer';
export type { PageFooterDesktopProps, ThreadFooterFirstRowProps } from './footer'; export type { PageFooterDesktopProps, CatalogFooterFirstRowProps, ThreadFooterFirstRowProps } from './footer';
@@ -28,12 +28,9 @@ const SiteLegalMeta = ({ order = 'version-first' }: SiteLegalMetaProps) => {
return ( return (
<> <>
<br />
{first} {first}
<br /> <br />
<br /> <span style={{ display: 'block', marginTop: 5 }}>{second}</span>
<br />
{second}
</> </>
); );
}; };
+21 -3
View File
@@ -386,11 +386,20 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
isInModView={isInModView} isInModView={isInModView}
showLoadingEllipsis={effectiveInfiniteScroll} showLoadingEllipsis={effectiveInfiniteScroll}
/> />
<PageFooterDesktop firstRow={<CatalogFooterFirstRow />} /> <PageFooterDesktop
firstRow={
<CatalogFooterFirstRow
subplebbitAddress={subplebbitAddress}
isInAllView={isInAllView}
isInSubscriptionsView={isInSubscriptionsView}
isInModView={isInModView}
/>
}
/>
</> </>
), ),
}), }),
[subplebbitAddresses, hasMore, cappedFeed.length, isInAllView, isInSubscriptionsView, isInModView, effectiveInfiniteScroll], [subplebbitAddresses, hasMore, cappedFeed.length, subplebbitAddress, isInAllView, isInSubscriptionsView, isInModView, effectiveInfiniteScroll],
); );
const isFeedLoaded = feed.length > 0 || state === 'failed'; const isFeedLoaded = feed.length > 0 || state === 'failed';
@@ -541,7 +550,16 @@ const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp,
error={error} error={error}
/> />
</div> </div>
<PageFooterDesktop firstRow={<CatalogFooterFirstRow />} /> <PageFooterDesktop
firstRow={
<CatalogFooterFirstRow
subplebbitAddress={subplebbitAddress}
isInAllView={isInAllView}
isInSubscriptionsView={isInSubscriptionsView}
isInModView={isInModView}
/>
}
/>
</> </>
)} )}
</div> </div>
+2 -1
View File
@@ -10,7 +10,7 @@ import { useDirectories } from '../../hooks/use-directories';
import { isDirectoryBoard } from '../../lib/utils/route-utils'; import { isDirectoryBoard } from '../../lib/utils/route-utils';
import useIsMobile from '../../hooks/use-is-mobile'; import useIsMobile from '../../hooks/use-is-mobile';
import ErrorDisplay from '../../components/error-display/error-display'; import ErrorDisplay from '../../components/error-display/error-display';
import { PageFooterDesktop, ThreadFooterFirstRow } from '../../components/footer'; import { PageFooterDesktop, ThreadFooterFirstRow, ThreadFooterStyleRow } from '../../components/footer';
import PostDesktop from '../../components/post-desktop'; import PostDesktop from '../../components/post-desktop';
import PostMobile from '../../components/post-mobile'; import PostMobile from '../../components/post-mobile';
import styles from './post.module.css'; import styles from './post.module.css';
@@ -205,6 +205,7 @@ const PostPage = () => {
{post?.cid && subplebbitAddress ? ( {post?.cid && subplebbitAddress ? (
<PageFooterDesktop <PageFooterDesktop
firstRow={<ThreadFooterFirstRow postCid={post.cid} threadNumber={post?.number} subplebbitAddress={subplebbitAddress} isThreadClosed={!!post?.locked} />} firstRow={<ThreadFooterFirstRow postCid={post.cid} threadNumber={post?.number} subplebbitAddress={subplebbitAddress} isThreadClosed={!!post?.locked} />}
styleRow={<ThreadFooterStyleRow />}
/> />
) : null} ) : null}
</div> </div>