mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix styling
This commit is contained in:
@@ -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 @@
|
|||||||
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 {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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 />
|
||||||
|
|||||||
@@ -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}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user