mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-09-20 16:42:58 +00:00
Merge branch 'master' of https://github.com/naturalcrit/homebrewery into fix-file-transfer
This commit is contained in:
@@ -308,7 +308,7 @@ const CodeEditor = forwardRef(
|
||||
view.dispatch({
|
||||
effects : themeCompartment.reconfigure(themeExtension),
|
||||
});
|
||||
}, [editorTheme]);
|
||||
}, [editorTheme, tab]);
|
||||
|
||||
useEffect(()=>{
|
||||
//rebuild syntax highlight when changing tab or renderer
|
||||
|
||||
@@ -92,12 +92,13 @@ const Dropdown = ({ groupName, className = null, icon, children, color = null, c
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={['menu-wrapper', className].join(' ')} role='none' >
|
||||
<li className='menu-wrapper' role='none'>
|
||||
<button
|
||||
id={`${menuId}-trigger`}
|
||||
className={['menu-item', color].join(' ')}
|
||||
popoverTarget={menuId}
|
||||
aria-haspopup='menu'
|
||||
aria-label={groupName}
|
||||
role='menuitem'
|
||||
disabled={!React.Children.count(children)}
|
||||
ref={triggerRef}
|
||||
@@ -105,18 +106,19 @@ const Dropdown = ({ groupName, className = null, icon, children, color = null, c
|
||||
{trigger(groupName, icon)}
|
||||
</button>
|
||||
<MenuDepthContext.Provider value={depth + 1}>
|
||||
<div
|
||||
<ul
|
||||
ref={menuRef}
|
||||
id={menuId}
|
||||
className='menu-list'
|
||||
popover='auto'
|
||||
role='menu'
|
||||
aria-label={`${groupName} Submenu`}
|
||||
onClick={handleMenuActionClick}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</ul>
|
||||
</MenuDepthContext.Provider>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
.menu-wrapper {
|
||||
position: relative;
|
||||
&:is(.menu-bar > .menu-section > .menu-wrapper){
|
||||
display: inline-block;
|
||||
}
|
||||
@property --menuColor {
|
||||
syntax: '<color>';
|
||||
inherits: true;
|
||||
initial-value: #DDD;
|
||||
}
|
||||
|
||||
@property --activeTriggerColor {
|
||||
syntax: '<color>';
|
||||
inherits: true;
|
||||
initial-value: #DDD;
|
||||
}
|
||||
|
||||
:root{
|
||||
--activeTriggerColor : var(--activeTriggerColor);
|
||||
}
|
||||
.menu-list {
|
||||
contain : content;
|
||||
position : fixed;
|
||||
z-index : 1000;
|
||||
top : anchor(bottom);
|
||||
left : anchor(left);
|
||||
position-try: flip-inline flip-block;
|
||||
color: inherit; // [popover] gets a `canvastext` color value from useragent.
|
||||
> .menu-wrapper {
|
||||
position:relative;
|
||||
> .menu-list {
|
||||
background: var(--menuColor);
|
||||
li > .menu-list {
|
||||
margin: 0 0px;
|
||||
top : anchor(top);
|
||||
left : anchor(right);
|
||||
position-try: flip-inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
.menu-wrapper:has(:popover-open) > button { // if menu is open...
|
||||
background-color: var(--activeTriggerColor, hsl(from var(--menuColor) h s calc(l * .85))); // tint menu triggers based on menu color
|
||||
}
|
||||
@@ -32,12 +32,12 @@ function safeHTML(htmlString) {
|
||||
return;
|
||||
}
|
||||
// Check remaining elements for blacklisted attributes
|
||||
for (const attribute of element.attributes){
|
||||
[...element.attributes].forEach((attribute)=>{
|
||||
if(blacklistAttrs.some((test)=>{return test(attribute);})) {
|
||||
element.removeAttribute(attribute.localName);
|
||||
break;
|
||||
element.removeAttribute(attribute.name);
|
||||
return;
|
||||
};
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return div.innerHTML;
|
||||
|
||||
@@ -99,11 +99,16 @@ const ToolBar = ({ displayOptions, onDisplayOptionsChange, visiblePages, totalPa
|
||||
return (
|
||||
<div id='preview-toolbar' className={`toolBar ${toolsVisible ? 'visible' : 'hidden'}`} role='toolbar'>
|
||||
<div className='toggleButton'>
|
||||
<button data-tooltip-right={`${toolsVisible ? 'Hide' : 'Show'} Preview Toolbar`} onClick={()=>{
|
||||
setToolsVisible(!toolsVisible);
|
||||
localStorage.setItem(TOOLBAR_VISIBILITY, !toolsVisible);
|
||||
}}><i className='fas fa-glasses' /></button>
|
||||
<button data-tooltip-right={`${headerState ? 'Hide' : 'Show'} Header Navigation`} onClick={()=>{setHeaderState(!headerState);}}><i className='fas fa-rectangle-list' /></button>
|
||||
<button data-tooltip-right={`${toolsVisible ? 'Hide' : 'Show'} Preview Toolbar`}
|
||||
aria-label={`${toolsVisible ? 'Hide' : 'Show'} Preview Toolbar`}
|
||||
onClick={()=>{ setToolsVisible(!toolsVisible); localStorage.setItem(TOOLBAR_VISIBILITY, !toolsVisible); }}>
|
||||
<i aria-hidden='true' className='fas fa-glasses' />
|
||||
</button>
|
||||
<button data-tooltip-right={`${headerState ? 'Hide' : 'Show'} Header Navigation`}
|
||||
aria-label={`${headerState ? 'Hide' : 'Show'} Header Navigation`}
|
||||
onClick={()=>{setHeaderState(!headerState);}}>
|
||||
<i aria-hidden='true' className='fas fa-rectangle-list' />
|
||||
</button>
|
||||
</div>
|
||||
{/*v=====----------------------< Zoom Controls >---------------------=====v*/}
|
||||
<div className='group' role='group' aria-label='Zoom' aria-hidden={!toolsVisible}>
|
||||
@@ -111,17 +116,19 @@ const ToolBar = ({ displayOptions, onDisplayOptionsChange, visiblePages, totalPa
|
||||
id='fill-width'
|
||||
className='tool'
|
||||
data-tooltip-bottom='Set zoom to fill preview with one page'
|
||||
aria-label='Set zoom to fill preview with one page'
|
||||
onClick={()=>handleZoomButton(displayOptions.zoomLevel + calculateChange('fill'))}
|
||||
>
|
||||
<i className='fac fit-width' />
|
||||
<i aria-hidden='true' className='fac fit-width' />
|
||||
</button>
|
||||
<button
|
||||
id='zoom-to-fit'
|
||||
className='tool'
|
||||
data-tooltip-bottom='Set zoom to fit entire page in preview'
|
||||
aria-label='Set zoom to fit entire page in preview'
|
||||
onClick={()=>handleZoomButton(displayOptions.zoomLevel + calculateChange('fit'))}
|
||||
>
|
||||
<i className='fac zoom-to-fit' />
|
||||
<i aria-hidden='true' className='fac zoom-to-fit' />
|
||||
</button>
|
||||
<button
|
||||
id='zoom-out'
|
||||
@@ -129,8 +136,9 @@ const ToolBar = ({ displayOptions, onDisplayOptionsChange, visiblePages, totalPa
|
||||
onClick={()=>handleZoomButton(displayOptions.zoomLevel - 20)}
|
||||
disabled={displayOptions.zoomLevel <= MIN_ZOOM}
|
||||
data-tooltip-bottom='Zoom Out'
|
||||
aria-label='Zoom Out'
|
||||
>
|
||||
<i className='fas fa-magnifying-glass-minus' />
|
||||
<i aria-hidden='true' className='fas fa-magnifying-glass-minus' />
|
||||
</button>
|
||||
<input
|
||||
id='zoom-slider'
|
||||
@@ -138,6 +146,7 @@ const ToolBar = ({ displayOptions, onDisplayOptionsChange, visiblePages, totalPa
|
||||
type='range'
|
||||
name='zoom'
|
||||
list='zoomLevels'
|
||||
aria-label='Zoom Amount'
|
||||
min={MIN_ZOOM}
|
||||
max={MAX_ZOOM}
|
||||
step='1'
|
||||
@@ -154,8 +163,9 @@ const ToolBar = ({ displayOptions, onDisplayOptionsChange, visiblePages, totalPa
|
||||
onClick={()=>handleZoomButton(displayOptions.zoomLevel + 20)}
|
||||
disabled={displayOptions.zoomLevel >= MAX_ZOOM}
|
||||
data-tooltip-bottom='Zoom In'
|
||||
aria-label='Zoom In'
|
||||
>
|
||||
<i className='fas fa-magnifying-glass-plus' />
|
||||
<i aria-hidden='true' className='fas fa-magnifying-glass-plus' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -166,27 +176,32 @@ const ToolBar = ({ displayOptions, onDisplayOptionsChange, visiblePages, totalPa
|
||||
id='single-spread'
|
||||
className='tool'
|
||||
data-tooltip-bottom='Single Page'
|
||||
aria-label='Single Page Spread'
|
||||
onClick={()=>{handleOptionChange('spread', 'single');}}
|
||||
aria-checked={displayOptions.spread === 'single'}
|
||||
><i className='fac single-spread' /></button>
|
||||
><i aria-hidden='true' className='fac single-spread' /></button>
|
||||
<button role='radio'
|
||||
id='facing-spread'
|
||||
className='tool'
|
||||
data-tooltip-bottom='Facing Pages'
|
||||
aria-label='Facing Pages Spread'
|
||||
onClick={()=>{handleOptionChange('spread', 'facing');}}
|
||||
aria-checked={displayOptions.spread === 'facing'}
|
||||
><i className='fac facing-spread' /></button>
|
||||
><i aria-hidden='true' className='fac facing-spread' /></button>
|
||||
<button role='radio'
|
||||
id='flow-spread'
|
||||
className='tool'
|
||||
data-tooltip-bottom='Flow Pages'
|
||||
aria-label='Flow Pages Spread'
|
||||
onClick={()=>{handleOptionChange('spread', 'flow');}}
|
||||
aria-checked={displayOptions.spread === 'flow'}
|
||||
><i className='fac flow-spread' /></button>
|
||||
><i aria-hidden='true' className='fac flow-spread' /></button>
|
||||
|
||||
</div>
|
||||
<Anchored>
|
||||
<AnchoredTrigger id='spread-settings' className='tool' data-tooltip-bottom='Spread options'><i className='fas fa-gear' /></AnchoredTrigger>
|
||||
<AnchoredTrigger id='spread-settings' className='tool' aria-label='Spread options' data-tooltip-bottom='Spread options'>
|
||||
<i aria-hidden='true' className='fas fa-gear' />
|
||||
</AnchoredTrigger>
|
||||
<AnchoredBox>
|
||||
<h1>Options</h1>
|
||||
<label data-tooltip-left='Modify the horizontal space between pages.'>
|
||||
@@ -217,10 +232,11 @@ const ToolBar = ({ displayOptions, onDisplayOptionsChange, visiblePages, totalPa
|
||||
className='previousPage tool'
|
||||
type='button'
|
||||
data-tooltip-bottom='Previous Page(s)'
|
||||
aria-label='Previous Page'
|
||||
onClick={()=>scrollToPage(_.min(visiblePages) - visiblePages.length)}
|
||||
disabled={visiblePages.includes(1)}
|
||||
>
|
||||
<i className='fas fa-arrow-left'></i>
|
||||
<i aria-hidden='true' className='fas fa-arrow-left'></i>
|
||||
</button>
|
||||
|
||||
<div className='tool'>
|
||||
@@ -230,6 +246,7 @@ const ToolBar = ({ displayOptions, onDisplayOptionsChange, visiblePages, totalPa
|
||||
type='text'
|
||||
name='page'
|
||||
data-tooltip-bottom='Current page(s) in view'
|
||||
aria-label='Current page in view'
|
||||
inputMode='numeric'
|
||||
pattern='[0-9]'
|
||||
value={pageNum}
|
||||
@@ -239,7 +256,7 @@ const ToolBar = ({ displayOptions, onDisplayOptionsChange, visiblePages, totalPa
|
||||
onKeyDown={(e)=>e.key == 'Enter' && scrollToPage(pageNum)}
|
||||
style={{ width: `${pageNum.length}ch` }}
|
||||
/>
|
||||
<span id='page-count' data-tooltip-bottom='Total Page Count'>/ {totalPages}</span>
|
||||
<span id='page-count' aria-label={`${totalPages} Total Pages`} data-tooltip-bottom='Total Page Count'><span aria-hidden='true'>/ {totalPages}</span></span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
@@ -247,10 +264,11 @@ const ToolBar = ({ displayOptions, onDisplayOptionsChange, visiblePages, totalPa
|
||||
className='tool'
|
||||
type='button'
|
||||
data-tooltip-bottom='Next Page(s)'
|
||||
aria-label='Next Page'
|
||||
onClick={()=>scrollToPage(_.max(visiblePages) + 1)}
|
||||
disabled={visiblePages.includes(totalPages)}
|
||||
>
|
||||
<i className='fas fa-arrow-right'></i>
|
||||
<i aria-hidden='true' className='fas fa-arrow-right'></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -188,7 +188,7 @@ const Snippetbar = createReactClass({
|
||||
const snippets = this.state.snippets.filter((snippetGroup)=>snippetGroup.view === this.props.view);
|
||||
if(snippets.length === 0) return null;
|
||||
|
||||
return <div className='snippets'>
|
||||
return <ul className='snippets' role='menubar' aria-label='Snippets Menubar'>
|
||||
{_.map(snippets, (snippetGroup)=>{
|
||||
return <SnippetGroup
|
||||
brew={this.props.brew}
|
||||
@@ -201,7 +201,7 @@ const Snippetbar = createReactClass({
|
||||
/>;
|
||||
})
|
||||
}
|
||||
</div>;
|
||||
</ul>;
|
||||
},
|
||||
|
||||
replaceContent : function(item){
|
||||
@@ -327,12 +327,14 @@ const SnippetGroup = createReactClass({
|
||||
return _.map(snippets, (snippet)=>{
|
||||
if(!snippet.subsnippets){
|
||||
return (
|
||||
<button className='menu-item' key={snippet.name} onClick={(e)=>this.handleSnippetClick(e, snippet)} role='menuitem'>
|
||||
<i className={snippet.icon} />
|
||||
<span className={`name${snippet.disabled ? ' disabled' : ''}`} title={snippet.name}>{snippet.name}</span>
|
||||
{snippet.experimental && <span className='beta'>beta</span>}
|
||||
{snippet.disabled && <span className='beta' title='temporarily disabled due to large slowdown; under re-design'>disabled</span>}
|
||||
</button>
|
||||
<li key={snippet.name} role='none'>
|
||||
<button className='menu-item' onClick={(e)=>this.handleSnippetClick(e, snippet)} role='menuitem' aria-label={snippet.name} disabled={snippet.disabled}>
|
||||
<i className={snippet.icon} />
|
||||
<span className={`name${snippet.disabled ? ' disabled' : ''}`} title={snippet.name}>{snippet.name}</span>
|
||||
{snippet.experimental && <span className='status'>beta</span>}
|
||||
{snippet.disabled && <span className='status' title='temporarily disabled due to large slowdown; under re-design'>disabled</span>}
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
} else if(snippet.subsnippets){
|
||||
return (
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
@import (less) '@themes/fonts/5e/fonts.less';
|
||||
|
||||
.snippetBar {
|
||||
--activeTriggerColor: inherit;
|
||||
--menuColor : #DDDDDD;
|
||||
@menuHeight : 25px;
|
||||
position : relative;
|
||||
display : flex;
|
||||
@@ -16,12 +18,6 @@
|
||||
text-transform: uppercase;
|
||||
font-weight: 800;
|
||||
|
||||
.snippets {
|
||||
display : flex;
|
||||
justify-content : flex-start;
|
||||
min-width : 499.35px; //must be controlled every time an item is added, must be hardcoded for the wrapping as it is applied
|
||||
}
|
||||
|
||||
.editors {
|
||||
display : flex;
|
||||
justify-content : flex-end;
|
||||
@@ -118,23 +114,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.snippetBarButton {
|
||||
display : inline-block;
|
||||
height : @menuHeight;
|
||||
padding : 0px 5px;
|
||||
font-size : 0.625em;
|
||||
font-weight : 800;
|
||||
line-height : @menuHeight;
|
||||
text-transform : uppercase;
|
||||
text-wrap : nowrap;
|
||||
cursor : pointer;
|
||||
&:hover, &.selected { background-color : #999999; }
|
||||
i {
|
||||
margin-right : 3px;
|
||||
font-size : 1.4em;
|
||||
vertical-align : middle;
|
||||
}
|
||||
}
|
||||
|
||||
.toggleMeta {
|
||||
position : absolute;
|
||||
top : 0px;
|
||||
@@ -143,20 +123,15 @@
|
||||
.tooltipLeft('Edit Brew Properties');
|
||||
}
|
||||
|
||||
|
||||
.menu-wrapper {
|
||||
.menu-item:is(.snippets > .menu-wrapper > .menu-item):first-child {
|
||||
.caret {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.menu-list {
|
||||
padding : 0px;
|
||||
background-color : #DDDDDD;
|
||||
|
||||
}
|
||||
.snippets {
|
||||
display : flex;
|
||||
justify-content : flex-start;
|
||||
min-width : 565.95px; //must be controlled every time an item is added, must be hardcoded for the wrapping as it is applied
|
||||
}
|
||||
|
||||
// removed caret for top level items, by request (makes buttons too wide).
|
||||
.menu-wrapper .menu-item:is(.snippets > .menu-wrapper > .menu-item):first-child .caret { display: none; }
|
||||
|
||||
.menu-item {
|
||||
position : relative;
|
||||
display : flex;
|
||||
@@ -166,9 +141,8 @@
|
||||
padding : 5px;
|
||||
cursor : pointer;
|
||||
width: 100%;
|
||||
.animate(background-color);
|
||||
&:is(.menu-list .menu-item) [class*="name"] {
|
||||
padding-inline: 8px;
|
||||
padding-inline: 8px; // additional space between icon and name (helpful in Fonts menu especially).
|
||||
}
|
||||
.menu-name {
|
||||
flex: 1;
|
||||
@@ -214,8 +188,7 @@
|
||||
}
|
||||
}
|
||||
.name { margin-right : auto; }
|
||||
.disabled { text-decoration : line-through; }
|
||||
.beta {
|
||||
.status {
|
||||
align-self : center;
|
||||
padding : 4px 6px;
|
||||
margin-left : 5px;
|
||||
@@ -234,15 +207,8 @@
|
||||
&:hover { background-color: unset; }
|
||||
}
|
||||
}
|
||||
.disabledSnippets {
|
||||
color: grey;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover { background-color: #DDDDDD;}
|
||||
}
|
||||
|
||||
}
|
||||
@container editor (width < 750px) {
|
||||
@container editor (width < 816px) {
|
||||
.snippetBar {
|
||||
.editors {
|
||||
flex : 1;
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
import './navbar.less';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import _ from 'lodash';
|
||||
import cx from 'classnames';
|
||||
|
||||
import NaturalCritIcon from '@components/svg/naturalcrit-d20.svg.jsx';
|
||||
|
||||
const Nav = {
|
||||
base : createReactClass({
|
||||
displayName : 'Nav.base',
|
||||
render : function(){
|
||||
return <nav>
|
||||
{this.props.children}
|
||||
</nav>;
|
||||
}
|
||||
}),
|
||||
logo : function(){
|
||||
base : ({ children, className, ...props })=>{
|
||||
return <nav className={className}>
|
||||
{children}
|
||||
</nav>;
|
||||
},
|
||||
logo : ()=>{
|
||||
return <a className='navLogo' href='https://www.naturalcrit.com'>
|
||||
<NaturalCritIcon />
|
||||
<span className='name'>
|
||||
@@ -24,50 +19,26 @@ const Nav = {
|
||||
</a>;
|
||||
},
|
||||
|
||||
section : createReactClass({
|
||||
displayName : 'Nav.section',
|
||||
render : function(){
|
||||
return <div className={`navSection ${this.props.className ?? ''}`}>
|
||||
{this.props.children}
|
||||
</div>;
|
||||
section : ({ children, className, ...props })=>{
|
||||
return <div className={cx([`navSection`, className])}>
|
||||
{children}
|
||||
</div>;
|
||||
},
|
||||
|
||||
item : ({ icon, href, newTab, onClick, color, children, className, ...props })=>{
|
||||
const classes = cx('navItem', color, className);
|
||||
if(href){
|
||||
return <a className={classes} href={href} target={newTab ? '_blank' : '_self'} {...props}>
|
||||
{children}
|
||||
{icon && <i className={icon}></i>}
|
||||
</a>;
|
||||
} else {
|
||||
return <button {...props} className={classes} onClick={onClick} >
|
||||
{children}
|
||||
{icon && <i className={icon}></i>}
|
||||
</button>;
|
||||
}
|
||||
}),
|
||||
|
||||
item : createReactClass({
|
||||
displayName : 'Nav.item',
|
||||
getDefaultProps : function() {
|
||||
return {
|
||||
icon : null,
|
||||
href : null,
|
||||
newTab : false,
|
||||
onClick : function(){},
|
||||
color : null
|
||||
};
|
||||
},
|
||||
handleClick : function(e){
|
||||
this.props.onClick(e);
|
||||
},
|
||||
render : function(){
|
||||
const classes = cx('navItem', this.props.color, this.props.className);
|
||||
|
||||
let icon;
|
||||
if(this.props.icon) icon = <i className={this.props.icon} />;
|
||||
|
||||
const props = _.omit(this.props, ['newTab']);
|
||||
|
||||
if(this.props.href){
|
||||
return <a {...props} className={classes} target={this.props.newTab ? '_blank' : '_self'} >
|
||||
{this.props.children}
|
||||
{icon}
|
||||
</a>;
|
||||
} else {
|
||||
return <button {...props} className={classes} onClick={this.handleClick} >
|
||||
{this.props.children}
|
||||
{icon}
|
||||
</button>;
|
||||
}
|
||||
}
|
||||
}),
|
||||
},
|
||||
|
||||
dropdown : function dropdown(props) {
|
||||
props = Object.assign({}, props, {
|
||||
|
||||
@@ -39,9 +39,9 @@ const BrewItem = ({
|
||||
if(!brew.editId) return null;
|
||||
|
||||
return (
|
||||
<a className='deleteLink' onClick={deleteBrew}>
|
||||
<i className='fas fa-trash-alt' title='Delete' />
|
||||
</a>
|
||||
<button aria-label={`Delete ${brew.title}`} className='deleteLink' onClick={deleteBrew}>
|
||||
<i className='fas fa-trash-alt' aria-hidden='true' title='Delete' />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@ const BrewItem = ({
|
||||
if(brew.googleId && !brew.stubbed) editLink = brew.googleId + editLink;
|
||||
|
||||
return (
|
||||
<a className='editLink' href={`/edit/${editLink}`} target='_blank' rel='noopener noreferrer'>
|
||||
<a className='editLink' href={`/edit/${editLink}`} aria-label={`Edit ${brew.title}`} target='_blank' rel='noopener noreferrer'>
|
||||
<i className='fas fa-pencil-alt' title='Edit' />
|
||||
</a>
|
||||
);
|
||||
@@ -67,7 +67,7 @@ const BrewItem = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<a className='shareLink' href={`/share/${shareLink}`} target='_blank' rel='noopener noreferrer'>
|
||||
<a className='shareLink' href={`/share/${shareLink}`} aria-label={`Share ${brew.title}`} target='_blank' rel='noopener noreferrer'>
|
||||
<i className='fas fa-share-alt' title='Share' />
|
||||
</a>
|
||||
);
|
||||
@@ -82,7 +82,7 @@ const BrewItem = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<a className='downloadLink' href={`/download/${shareLink}`}>
|
||||
<a className='downloadLink' aria-label={`Download ${brew.title}`} href={`/download/${shareLink}`}>
|
||||
<i className='fas fa-download' title='Download' />
|
||||
</a>
|
||||
);
|
||||
@@ -94,7 +94,7 @@ const BrewItem = ({
|
||||
return (
|
||||
<span title={brew.webViewLink ? 'Your Google Drive Storage' : 'Another User\'s Google Drive Storage'}>
|
||||
<a href={brew.webViewLink} target='_blank'>
|
||||
<img className='googleDriveIcon' src={googleDriveIcon} alt='googleDriveIcon' />
|
||||
<img className='googleDriveIcon' src={googleDriveIcon} alt='Google Drive Storage' />
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
@@ -102,7 +102,7 @@ const BrewItem = ({
|
||||
|
||||
return (
|
||||
<span title='Homebrewery Storage'>
|
||||
<img className='homebreweryIcon' src={homebreweryIcon} alt='homebreweryIcon' />
|
||||
<img className='homebreweryIcon' src={homebreweryIcon} alt='Homebrewery Storage' />
|
||||
</span>
|
||||
);
|
||||
};
|
||||
@@ -148,19 +148,20 @@ const BrewItem = ({
|
||||
))}
|
||||
</span>
|
||||
<br />
|
||||
<span title={`Last viewed: ${moment(brew.lastViewed).local().format(dateFormatString)}`}>
|
||||
<i className='fas fa-eye' /> {brew.views}
|
||||
<span aria-label={`Viewed ${brew.views} times`} title={`Last viewed: ${moment(brew.lastViewed).local().format(dateFormatString)}`}>
|
||||
<span aria-hidden='true'><i className='fas fa-eye' /> {brew.views}</span>
|
||||
</span>
|
||||
{brew.pageCount && (
|
||||
<span title={`Page count: ${brew.pageCount}`}>
|
||||
<i className='far fa-file' /> {brew.pageCount}
|
||||
<span aria-label={`${brew.pageCount} pages`} title={`Page count: ${brew.pageCount}`}>
|
||||
<span aria-hidden='true'><i className='far fa-file' /> {brew.pageCount}</span>
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
aria-label={`Last updated ${moment(brew.updatedAt).fromNow()}`}
|
||||
title={dedent` Created: ${moment(brew.createdAt).local().format(dateFormatString)}
|
||||
Last updated: ${moment(brew.updatedAt).local().format(dateFormatString)}`}
|
||||
>
|
||||
<i className='fas fa-sync-alt' /> {moment(brew.updatedAt).fromNow()}
|
||||
<span aria-hidden='true'><i className='fas fa-sync-alt' /> {moment(brew.updatedAt).fromNow()}</span>
|
||||
</span>
|
||||
{renderStorageIcon()}
|
||||
</div>
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
&::before { content : '\f518'; }
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
&:hover, &:focus-within {
|
||||
.links { opacity : 1; }
|
||||
}
|
||||
&:nth-child(2n + 1) { margin-right : 0px; }
|
||||
@@ -103,7 +103,7 @@
|
||||
text-align : center;
|
||||
background-color : fade(black, 60%);
|
||||
opacity : 0;
|
||||
a {
|
||||
a, button {
|
||||
.animate(opacity);
|
||||
display : block;
|
||||
margin : 8px 0px;
|
||||
@@ -111,6 +111,7 @@
|
||||
color : white;
|
||||
text-decoration : unset;
|
||||
opacity : 0.6;
|
||||
width : 100%;
|
||||
&:hover { opacity : 1; }
|
||||
i { cursor : pointer; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user