Merge pull request #5111 from naturalcrit/fix-vault-styles

Fix vault styles
This commit is contained in:
Víctor Losada Hernández
2026-10-13 18:42:30 +02:00
committed by GitHub
4 changed files with 274 additions and 153 deletions
@@ -335,7 +335,7 @@
} }
//these media queries are just for the list layout and could be moved inside it, not sure it is a good idea //these media queries are just for the list layout and could be moved inside it, not sure it is a good idea
@media screen and (max-width : 1400px) { @container (max-width : 1400px) {
.brewCollection .brewGroup.list { .brewCollection .brewGroup.list {
.brewItem:not(.detailsOpen) { .brewItem:not(.detailsOpen) {
.info { .info {
@@ -361,7 +361,7 @@
} }
} }
} }
@media screen and (max-width : 1200px) { @container (max-width : 1200px) {
.brewCollection .brewGroup.list { .brewCollection .brewGroup.list {
.brewItem:not(.detailsOpen) { .brewItem:not(.detailsOpen) {
grid-template-columns : 35% 3fr 150px; grid-template-columns : 35% 3fr 150px;
@@ -388,7 +388,7 @@
} }
} }
} }
@media screen and (max-width : 800px) { @container (max-width : 800px) {
.brewCollection .brewGroup.list { .brewCollection .brewGroup.list {
.brewItem:not(.detailsOpen) { .brewItem:not(.detailsOpen) {
.info { .info {
@@ -443,7 +443,7 @@
} }
} }
} }
@media screen and (max-width : 650px) { @container (max-width : 650px) {
.brewCollection .brewGroup.list { .brewCollection .brewGroup.list {
.brewItem:not(.detailsOpen) { .brewItem:not(.detailsOpen) {
grid-template-columns : 3fr 2fr; grid-template-columns : 3fr 2fr;
@@ -21,15 +21,16 @@
.sitePage.listPage { .sitePage.listPage {
--bg-clr : white; --bg-clr : white;
container-type: inline-size;
.content { .content {
padding : clamp(20px, 5vh, 200px) clamp(20px, 20vw, 200px); padding : clamp(20px, 5vh, 200px) clamp(20px, 20cqi, 200px);
overflow-y : scroll; overflow-y : scroll;
scrollbar-gutter : stable; scrollbar-gutter : stable;
@media screen and (max-width : 1200px) { @container (max-width : 1200px) {
padding : clamp(20px, 5vh, 200px) clamp(20px, 10vw, 200px); padding : clamp(20px, 5vh, 200px) clamp(20px, 10cqi, 200px);
} }
@media screen and (max-width : 800px) { @container (max-width : 800px) {
padding : clamp(20px, 5vh, 200px) clamp(20px, 5vw, 200px); padding : clamp(20px, 5vh, 200px) clamp(20px, 5cqi, 200px);
} }
&::-webkit-scrollbar { &::-webkit-scrollbar {
@@ -53,6 +54,7 @@
color : white; color : white;
} }
.brewCollection { .brewCollection {
h1 { h1 {
width : fit-content; width : fit-content;
margin-bottom : 20px; margin-bottom : 20px;
@@ -97,9 +99,9 @@
gap : 20px; gap : 20px;
justify-items : center; justify-items : center;
@media screen and (max-width : 1700px) {--columns : 3;} @container (max-width : 1700px) {--columns : 3;}
@media screen and (max-width : 1400px) {--columns : 2;} @container (max-width : 1400px) {--columns : 2;}
@media screen and (max-width : 900px) {--columns : 1;} @container (max-width : 900px) {--columns : 1;}
h1 { position : absolute; } h1 { position : absolute; }
&:not(:has(.brewItem:last-child:nth-child(3n))) { &:not(:has(.brewItem:last-child:nth-child(3n))) {
+96 -17
View File
@@ -16,6 +16,13 @@ import ErrorIndex from '../errorPage/errors/errorIndex.js';
import request from '../../utils/request-middleware.js'; import request from '../../utils/request-middleware.js';
const DEFAULT_SORT_TYPE = 'alpha';
const DEFAULT_SORT_DIR = 'asc';
const DEFAULT_LAYOUT_MODE = 'grid';
const DEFAULT_THEME_MODE = 'light';
const USERPAGE_LAYOUT_MODE = 'HB_listPage_layout_mode';
const USERPAGE_THEME_MODE = 'HB_listPage_theme_mode';
const VaultPage = (props)=>{ const VaultPage = (props)=>{
const [pageState, setPageState] = useState(parseInt(props.query.page) || 1); const [pageState, setPageState] = useState(parseInt(props.query.page) || 1);
@@ -27,6 +34,8 @@ const VaultPage = (props)=>{
const [totalBrews, setTotalBrews] = useState(null); const [totalBrews, setTotalBrews] = useState(null);
const [searching, setSearching] = useState(false); const [searching, setSearching] = useState(false);
const [error, setError] = useState(null); const [error, setError] = useState(null);
const [layoutMode, setLayoutMode] = useState(DEFAULT_LAYOUT_MODE);
const [themeMode, setThemeMode] = useState(DEFAULT_THEME_MODE);
const titleRef = useRef(null); const titleRef = useRef(null);
const authorRef = useRef(null); const authorRef = useRef(null);
@@ -37,6 +46,13 @@ const VaultPage = (props)=>{
useEffect(()=>{ useEffect(()=>{
disableSubmitIfFormInvalid(); disableSubmitIfFormInvalid();
const newLayoutMode = localStorage.getItem(USERPAGE_LAYOUT_MODE) || DEFAULT_LAYOUT_MODE;
const newThemeMode = localStorage.getItem(USERPAGE_THEME_MODE) || DEFAULT_THEME_MODE;
setLayoutMode(newLayoutMode);
setThemeMode(newThemeMode);
loadPage(pageState, true, props.query.sort, props.query.dir); loadPage(pageState, true, props.query.sort, props.query.dir);
}, []); }, []);
@@ -83,7 +99,8 @@ const VaultPage = (props)=>{
const response = await request.get(`/api/vault/total?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}`) const response = await request.get(`/api/vault/total?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}`)
.catch((error)=>{ .catch((error)=>{
console.log('error at loadTotal: ', error); console.log('error at loadTotal: ');
console.error(error);
setError(error); setError(error);
updateStateWithBrews([], 1); updateStateWithBrews([], 1);
}); });
@@ -261,7 +278,7 @@ const VaultPage = (props)=>{
const oppositeDir = dirState === 'asc' ? 'desc' : 'asc'; const oppositeDir = dirState === 'asc' ? 'desc' : 'asc';
return ( return (
<div className={`sort-option ${sortState === optionValue ? `active` : ''}`}> <div className={`option ${sortState === optionValue ? `active` : ''}`}>
<button onClick={()=>loadPage(1, false, optionValue, oppositeDir)}> <button onClick={()=>loadPage(1, false, optionValue, oppositeDir)}>
{optionTitle} {optionTitle}
</button> </button>
@@ -272,10 +289,10 @@ const VaultPage = (props)=>{
); );
}; };
const renderSortBar = ()=>{ const renderSortOptions = ()=>{
return ( return (
<div className='sort-container'> <div className='radio-toggle sort-container'>
{renderSortOption('Title', 'title', props.query.dir)} {renderSortOption('Title', 'title', props.query.dir)}
{renderSortOption('Created Date', 'createdAt', props.query.dir)} {renderSortOption('Created Date', 'createdAt', props.query.dir)}
{renderSortOption('Updated Date', 'updatedAt', props.query.dir)} {renderSortOption('Updated Date', 'updatedAt', props.query.dir)}
@@ -284,6 +301,64 @@ const VaultPage = (props)=>{
); );
}; };
const handleLayoutChange = (e, mode)=>{
setLayoutMode(e.target.checked ? mode : DEFAULT_LAYOUT_MODE);
localStorage.setItem(USERPAGE_LAYOUT_MODE, e.target.checked ? mode : DEFAULT_LAYOUT_MODE);
return;
};
const renderLayoutModeOptions = ()=>{
return (
<div className='radio-toggle layout-container'>
<h6>View:</h6>
<label className='option' title='grid'>
<input name='layout-mode' type='radio' onChange={(e)=>handleLayoutChange(e, 'grid')} checked={layoutMode === 'grid'} />
<i className='fac compressed-grid'></i>
</label>
<label className='option' title='list'>
<input name='layout-mode' type='radio' onChange={(e)=>handleLayoutChange(e, 'list')} checked={layoutMode === 'list'} />
<i className='fas fa-bars'></i>
</label>
<label className='option' title='card'>
<input name='layout-mode' type='radio' onChange={(e)=>handleLayoutChange(e, 'card')} checked={layoutMode === 'card'} />
<i className='fac format-card'></i>
</label>
</div>
);
};
const handleThemeChange = (e, mode)=>{
setThemeMode(e.target.checked ? mode : DEFAULT_THEME_MODE);
localStorage.setItem(USERPAGE_THEME_MODE, e.target.checked ? mode : DEFAULT_THEME_MODE);
return;
};
const renderThemePicker = ()=>{
return (
<div className="radio-toggle theme-container">
<h6>Theme:</h6>
<label className="option">
<input type="radio" name="theme" onChange={(e)=>handleThemeChange(e, 'light')} checked={themeMode === 'light'}/>
<i className="fas fa-sun"/>
</label>
<label className="option">
<input type="radio" name="theme" onChange={(e)=>handleThemeChange(e, 'dark')} checked={themeMode === 'dark'}/>
<i className="fas fa-moon"/>
</label>
</div>
)
};
const renderNav = () =>{
return (
<div className="listPageNav">
{renderLayoutModeOptions()}
{renderThemePicker()}
{renderSortOptions()}
</div>
);
}
const renderPaginationControls = ()=>{ const renderPaginationControls = ()=>{
if(!totalBrews || totalBrews < 10) return null; if(!totalBrews || totalBrews < 10) return null;
@@ -367,7 +442,7 @@ const VaultPage = (props)=>{
if(error.HBErrorCode === undefined) { if(error.HBErrorCode === undefined) {
return ( return (
<div className='foundBrews noBrews'> <div className='foundBrews noBrews'>
<h3>{error.toString()}</h3> <h3>Error: Unknown error</h3>
</div> </div>
) )
} else { } else {
@@ -400,22 +475,26 @@ const VaultPage = (props)=>{
} }
return ( return (
<div className='foundBrews'> <div className={`foundBrews ${themeMode + 'Mode'}`}>
<span className='totalBrews'> <span className='totalBrews'>
{`Brews found: `} {`Brews found: `}
<span>{totalBrews}</span> <span>{totalBrews}</span>
</span> </span>
{brewCollection.length > 10 && renderPaginationControls()} {brewCollection.length > 10 && renderPaginationControls()}
{brewCollection.map((brew, index)=>{ <div className='brewCollection'>
return ( <div className={`brewGroup ${layoutMode}`}>
<BrewItem {brewCollection.map((brew, index)=>{
brew={{ ...brew }} return (
key={index} <BrewItem
reportError={props.reportError} brew={{ ...brew }}
renderStorage={false} key={index}
/> reportError={props.reportError}
); renderStorage={false}
})} />
);
})}
</div>
</div>
{renderPaginationControls()} {renderPaginationControls()}
</div> </div>
); );
@@ -430,7 +509,7 @@ const VaultPage = (props)=>{
<SplitPane showDividerButtons={false}> <SplitPane showDividerButtons={false}>
<div className='form dataGroup'>{renderForm()}</div> <div className='form dataGroup'>{renderForm()}</div>
<div className='resultsContainer dataGroup'> <div className='resultsContainer dataGroup'>
{renderSortBar()} {renderNav()}
{renderFoundBrews()} {renderFoundBrews()}
</div> </div>
</SplitPane> </SplitPane>
+163 -123
View File
@@ -117,69 +117,40 @@
height : 100%; height : 100%;
overflow-y : auto; overflow-y : auto;
font-size : 0.34cm; font-size : 0.34cm;
container-type : inline-size;
h3 {
font-family : 'Open Sans';
font-weight : 900;
color : white;
}
.sort-container {
display : flex;
flex-wrap : wrap;
column-gap : 15px;
justify-content : center;
height : 30px;
color : white;
background-color : #555555;
border-top : 1px solid #666666;
border-bottom : 1px solid #666666;
.sort-option {
display : flex;
align-items : center;
padding : 0 8px;
&:hover { background-color : #444444; }
&.active {
background-color : #333333;
button {
font-weight : 800;
color : white;
& + .sortDir { padding-left : 5px; }
}
}
button {
padding : 0;
font-size : 11px;
font-weight : normal;
color : #CCCCCC;
text-transform : uppercase;
background-color : transparent;
&:hover { background : none; }
}
}
}
.foundBrews { .foundBrews {
position : relative; display:flex;
width : 100%; flex-direction: column;
height : 100%; gap:20px;
max-height : 100%; padding : clamp(20px, 5vh, 200px) clamp(20px, 20vw, 200px);
padding : 70px 50px;
container-type : inline-size;
overflow-y : scroll; overflow-y : scroll;
scrollbar-gutter : stable;
@container (max-width : 1200px) {
padding : clamp(20px, 5vh, 200px) clamp(20px, 10vw, 200px);
}
@container (max-width : 800px) {
padding : clamp(20px, 5vh, 200px) clamp(20px, 5vw, 200px);
}
h3 { font-size : 25px; } &::-webkit-scrollbar {
width : 15px;
&:horizontal {
width : auto;
height : 20px;
}
&-thumb {
background : linear-gradient(90deg, var(--bg-clr) 12px, #00000000 12px);
&:horizontal { background : linear-gradient(0deg, var(--bg-clr) 15px, #00000000 15px); }
}
&-corner { visibility : hidden; }
}
&.noBrews { &.noBrews {
display : grid; margin : 10px 0px;
place-items : center; font-size : 25px;
font-style : italic;
font-weight : 900;
color : white; color : white;
} }
@@ -202,6 +173,64 @@
} }
} }
.brewCollection {
h1 {
width : fit-content;
margin-bottom : 20px;
font-size : 25px;
color : #DDDDDD;
&.active::before,
&.inactive::before {
padding-right : 0.5em;
font-family : 'Font Awesome 6 Free';
font-size : 0.6cm;
font-weight : 900;
}
&.active {
color : white;
&::before { content : '\f107'; }
}
&.inactive::before { content : '\f105'; }
}
h1:hover { cursor : pointer; }
.brewGroup {
margin-bottom : 20px;
&.grid {
display : flex;
flex-wrap : wrap;
gap : 20px;
justify-content : center;
width : fit-content;
min-width : 200px;
}
&.list {
display : flex;
flex-direction : column;
gap : 2px;
}
&.card {
--columns : 4;
display : grid;
grid-template-columns : repeat(var(--columns), 1fr);
gap : 20px;
justify-items : center;
@container (max-width : 1700px) {--columns : 3;}
@container (max-width : 1400px) {--columns : 2;}
@container (max-width : 900px) {--columns : 1;}
h1 { position : absolute; }
&:not(:has(.brewItem:last-child:nth-child(3n))) {
.brewItem:last-child { margin-right : auto; }
}
}
}
}
.totalBrews { .totalBrews {
position : fixed; position : fixed;
right : 0; right : 0;
@@ -233,66 +262,15 @@
} }
} }
.brewItem {
width : 47%;
margin-right : 40px;
color : black;
isolation : isolate;
transition : width 0.5s;
&::after {
position : absolute;
inset : 0;
z-index : -2;
display : block;
content : '';
background-image : url('/assets/parchmentBackground.jpg');
}
&:nth-child(even of .brewItem) { margin-right : 0; }
h2 {
font-family : 'MrEavesRemake';
font-size : 0.75cm;
font-weight : 800;
line-height : 0.988em;
color : var(--HB_Color_HeaderText);
}
.info {
position : relative;
z-index : 2;
font-family : 'ScalySansRemake';
font-size : 1.2em;
>span {
margin-right : 12px;
line-height : 1.5em;
}
}
.links { z-index : 2; }
hr {
visibility : hidden;
margin : 0px;
}
.thumbnail { z-index : -1; }
}
.paginationControls { .paginationControls {
position : absolute;
top : 35px;
left : 50%;
display : grid; display : grid;
grid-template-areas : 'previousPage currentPage nextPage'; grid-template-areas : 'previousPage currentPage nextPage';
grid-template-columns : 50px 1fr 50px; grid-template-columns : 50px 1fr 50px;
gap : 20px; gap : 20px;
place-items : center; place-items : center;
width : auto; width : fit-content;
margin-inline:auto;
font-size : 15px; font-size : 15px;
translate : -50%;
&:last-child { top : unset; }
.pages { .pages {
display : flex; display : flex;
@@ -314,8 +292,11 @@
text-underline-position : under; text-underline-position : under;
cursor : pointer; cursor : pointer;
&.currentPage { &:hover {
color : gold; color : gold;
}
&.currentPage {
color:@yellow;
text-decoration : underline; text-decoration : underline;
pointer-events : none; pointer-events : none;
} }
@@ -339,6 +320,77 @@
} }
} }
} }
.listPageNav {
position : relative;
display : flex;
flex-wrap : wrap;
row-gap : 10px;
column-gap : 20px;
align-items : center;
justify-content : center;
width : 100%;
height : fit-content;
font-family : 'Open Sans', sans-serif;
color : white;
background : #555555;
border-top : 1px solid #666666;
border-bottom : 1px solid #666666;
row-rule : 1px solid #666666;
> div {
width : fit-content;
height : 30px;
padding-inline : 10px;
}
.radio-toggle {
z-index : 1;
display : flex;
flex-wrap : wrap;
row-gap : 5px;
align-items : center;
justify-content : center;
text-align : center;
h6 {
margin-right : 5px;
font-size : 14px;
}
.option {
display : flex;
align-items : center;
height : 100%;
padding : 0 8px;
color : #CCCCCC;
input[type='radio'] { display : none; }
button {
padding : 0;
font-size : 11px;
font-weight : normal;
text-transform : uppercase;
background-color : transparent;
}
&:hover { background-color : #444444; }
&.active, &:has(input:checked) {
font-weight : bold;
color : white;
background-color : #333333;
button {
height : 100%;
font-weight : 800;
& + .sortDir { padding-left : 5px; }
}
}
}
}
}
} }
@keyframes trailingDots { @keyframes trailingDots {
@@ -352,15 +404,3 @@
66%, 66%,
100% { content : ' ...'; } 100% { content : ' ...'; }
} }
@container (width < 670px) {
.vaultPage {
.dataGroup.form .brewLookup { padding : 1px 20px 20px 10px; }
.dataGroup.resultsContainer .foundBrews .brewItem {
width : 100%;
margin-inline : auto;
}
}
}