mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
refactor(ui): rename topbar to boardsbar and add desktop footer for board, thread, and catalog
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.boardsbarEditDialog {
|
||||
position: absolute;
|
||||
width: 400px;
|
||||
max-height: 80vh;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -200px;
|
||||
transform: translateY(-50%);
|
||||
padding: 2px;
|
||||
background-color: var(--settings-modal-background-color);
|
||||
border-top: var(--settings-modal-border-top);
|
||||
border-right: var(--settings-modal-border-right);
|
||||
border-bottom: var(--settings-modal-border-bottom);
|
||||
border-left: var(--settings-modal-border-left);
|
||||
box-shadow: rgba(0, 0, 0, 0.25) 0px 0px 5px 0px;
|
||||
font-size: var(--body-font-size);
|
||||
line-height: normal;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.boardsbarEditDialog {
|
||||
width: calc(100% - 20px);
|
||||
margin-left: -50%;
|
||||
left: 50%;
|
||||
max-width: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
.hd {
|
||||
position: relative;
|
||||
padding: 0px 0px 5px;
|
||||
margin: 5px 0px;
|
||||
text-align: center;
|
||||
border-bottom: var(--settings-modal-header-border-bottom);
|
||||
}
|
||||
|
||||
.hd h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.closeButton {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: 3px;
|
||||
background-image: var(--settings-modal-close-button-background-image);
|
||||
background-size: 100%;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
.bd {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section h3 {
|
||||
margin: 0 0 8px 0;
|
||||
padding: 0;
|
||||
font-size: calc(var(--body-font-size) * 1.077);
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.directoryInput {
|
||||
width: 100%;
|
||||
padding: 2px 4px 3px;
|
||||
font-size: 13.3333px;
|
||||
margin: 0 0 5px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
font-family: Arial, sans-serif;
|
||||
border: var(--post-form-field-input-border, revert);
|
||||
background-color: var(--post-form-field-input-background-color, revert);
|
||||
color: var(--post-form-field-input-color);
|
||||
}
|
||||
|
||||
.directoryInput:focus {
|
||||
outline: none;
|
||||
border: var(--post-form-field-input-focus-border, revert);
|
||||
}
|
||||
|
||||
.checkboxGroup {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.checkboxItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.checkboxItem input[type='checkbox'] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkboxItem label {
|
||||
cursor: pointer;
|
||||
font-size: var(--body-font-size);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.editSubscriptionsLink {
|
||||
font-size: var(--body-font-size);
|
||||
color: var(--post-link-text-color);
|
||||
text-decoration: var(--post-content-link-text-decoration);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.editSubscriptionsWrapper {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.editSubscriptionsLink:hover {
|
||||
color: var(--post-link-text-color-hover);
|
||||
text-decoration: var(--post-content-link-text-decoration-hover);
|
||||
}
|
||||
|
||||
.boardsbarEditFooter {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import useBoardsBarEditModalStore from '../../stores/use-boardsbar-edit-modal-store';
|
||||
import useBoardsBarVisibilityStore from '../../stores/use-boardsbar-visibility-store';
|
||||
import { getAllBoardCodes } from '../../constants/board-codes';
|
||||
import styles from './boardsbar-edit-modal.module.css';
|
||||
|
||||
const directoriesToString = (dirs: Set<string>): string => Array.from(dirs).sort().join(' ');
|
||||
|
||||
const stringToDirectories = (str: string): Set<string> => {
|
||||
const codes = str
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter((code) => code.length > 0)
|
||||
.map((code) => code.toLowerCase());
|
||||
return new Set(codes);
|
||||
};
|
||||
|
||||
// Form component keyed by store values so it remounts with fresh state when modal reopens
|
||||
const BoardsBarEditModalForm = ({
|
||||
visibleDirectories,
|
||||
showSubscriptionsInBoardsBar,
|
||||
setDirectoryVisibility,
|
||||
setShowSubscriptionsInBoardsBar,
|
||||
closeBoardsBarEditModal,
|
||||
subscriptions,
|
||||
location,
|
||||
}: {
|
||||
visibleDirectories: Set<string>;
|
||||
showSubscriptionsInBoardsBar: boolean;
|
||||
setDirectoryVisibility: (code: string, visible: boolean) => void;
|
||||
setShowSubscriptionsInBoardsBar: (show: boolean) => void;
|
||||
closeBoardsBarEditModal: () => void;
|
||||
subscriptions: string[];
|
||||
location: { pathname: string };
|
||||
}) => {
|
||||
const allBoardCodes = useMemo(() => getAllBoardCodes(), []);
|
||||
const allVisible = allBoardCodes.every((code) => visibleDirectories.has(code));
|
||||
|
||||
const [localDirectoryInput, setLocalDirectoryInput] = useState(() => (allVisible ? '' : directoriesToString(visibleDirectories)));
|
||||
const [showSubscriptions, setShowSubscriptions] = useState(() => showSubscriptionsInBoardsBar);
|
||||
|
||||
const handleSave = () => {
|
||||
if (localDirectoryInput.trim() === '') {
|
||||
allBoardCodes.forEach((code) => setDirectoryVisibility(code, true));
|
||||
} else {
|
||||
const inputDirectories = stringToDirectories(localDirectoryInput);
|
||||
allBoardCodes.forEach((code) => setDirectoryVisibility(code, inputDirectories.has(code)));
|
||||
}
|
||||
setShowSubscriptionsInBoardsBar(showSubscriptions);
|
||||
closeBoardsBarEditModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.section}>
|
||||
<input
|
||||
type='text'
|
||||
className={styles.directoryInput}
|
||||
placeholder='Example: jp tg mu'
|
||||
aria-label='Directory codes'
|
||||
value={localDirectoryInput}
|
||||
onChange={(e) => setLocalDirectoryInput(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{subscriptions.length > 0 && (
|
||||
<div className={styles.section}>
|
||||
<div className={styles.checkboxItem}>
|
||||
<input type='checkbox' id='show-subscriptions' checked={showSubscriptions} onChange={(e) => setShowSubscriptions(e.target.checked)} />
|
||||
<label htmlFor='show-subscriptions'>show subscriptions</label>
|
||||
<span className={styles.editSubscriptionsWrapper}>
|
||||
(
|
||||
<Link
|
||||
to={location.pathname.replace(/\/$/, '') + '/settings#subscriptions-settings'}
|
||||
className={styles.editSubscriptionsLink}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
closeBoardsBarEditModal();
|
||||
}}
|
||||
>
|
||||
edit subscriptions
|
||||
</Link>
|
||||
)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.boardsbarEditFooter}>
|
||||
<button onClick={handleSave}>Save</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const BoardsBarEditModal = () => {
|
||||
const { showModal, closeBoardsBarEditModal } = useBoardsBarEditModalStore();
|
||||
const { visibleDirectories, showSubscriptionsInBoardsBar, setDirectoryVisibility, setShowSubscriptionsInBoardsBar } = useBoardsBarVisibilityStore();
|
||||
const account = useAccount();
|
||||
const subscriptions = useMemo(() => account?.subscriptions || [], [account?.subscriptions]);
|
||||
const location = useLocation();
|
||||
|
||||
if (!showModal) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
closeBoardsBarEditModal();
|
||||
}
|
||||
};
|
||||
|
||||
const formKey = `${directoriesToString(visibleDirectories)}-${showSubscriptionsInBoardsBar}`;
|
||||
|
||||
return (
|
||||
<div className={styles.backdrop} onClick={handleBackdropClick}>
|
||||
<div className={styles.boardsbarEditDialog}>
|
||||
<div className={styles.hd}>
|
||||
<h2>Custom Board List</h2>
|
||||
<button className={styles.closeButton} onClick={closeBoardsBarEditModal} title='Close' />
|
||||
</div>
|
||||
<div className={styles.bd}>
|
||||
<BoardsBarEditModalForm
|
||||
key={formKey}
|
||||
visibleDirectories={visibleDirectories}
|
||||
showSubscriptionsInBoardsBar={showSubscriptionsInBoardsBar}
|
||||
setDirectoryVisibility={setDirectoryVisibility}
|
||||
setShowSubscriptionsInBoardsBar={setShowSubscriptionsInBoardsBar}
|
||||
closeBoardsBarEditModal={closeBoardsBarEditModal}
|
||||
subscriptions={subscriptions}
|
||||
location={location}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BoardsBarEditModal;
|
||||
@@ -0,0 +1,13 @@
|
||||
// Types for boardsbar edit modal component
|
||||
|
||||
export interface BoardCodeCheckbox {
|
||||
code: string;
|
||||
label: string;
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
export interface SubscriptionCheckbox {
|
||||
address: string;
|
||||
displayName: string;
|
||||
checked: boolean;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './boardsbar-edit-modal';
|
||||
Reference in New Issue
Block a user