Factor out reusable MobileTooltip component

Also prevents tabIndex from being generated on desktop
This commit is contained in:
Quantum 2023-04-29 06:18:48 -04:00
parent 1a7c4915e1
commit 8c238c6625
10 changed files with 76 additions and 54 deletions

View file

@ -0,0 +1,20 @@
@media (pointer: coarse), (hover: none) {
.MobileTooltip {
position: relative;
display: inline-flex;
justify-content: center;
&:focus::after {
content: attr(title);
position: absolute;
top: 90%;
left: 0;
color: #000;
background-color: #fff;
border: 1px solid;
width: fit-content;
padding: 3px;
z-index: 10;
}
}
}

View file

@ -0,0 +1,31 @@
import * as React from 'react';
import {useEffect} from 'react';
const MobileTooltipContext = React.createContext(false);
export function MobileTooltipProvider({children}: React.PropsWithChildren<{}>): JSX.Element {
const [mobile, setMobile] = React.useState(false);
useEffect(() => {
if (!window.matchMedia)
return;
const match = window.matchMedia('(pointer: coarse), (hover: none)');
const callback = () => setMobile(match.matches);
callback();
match.addEventListener('change', callback);
return () => match.removeEventListener('change', callback);
}, [setMobile]);
return <MobileTooltipContext.Provider value={mobile}>
{children}
</MobileTooltipContext.Provider>;
}
export function useMobileTooltipProps(): { className: string, tabIndex?: number } {
return {
className: 'MobileTooltip',
...React.useContext(MobileTooltipContext) ? {tabIndex: 0} : {},
};
}

View file

@ -147,26 +147,6 @@
background: $gray-300; background: $gray-300;
} }
@media (pointer: coarse), (hover: none) {
.Day-rural {
position: relative;
display: inline-flex;
justify-content: center;
text-decoration: underline dashed;
}
.Day-rural:focus::after {
content: attr(title);
position: absolute;
top: 90%;
color: #000;
background-color: #fff;
border: 1px solid;
width: fit-content;
padding: 3px;
}
}
.Calendar-month-name.input-group { .Calendar-month-name.input-group {
justify-content: center; justify-content: center;
font-size: 0.75em; font-size: 0.75em;

View file

@ -15,6 +15,7 @@ import {
} from '@common/french'; } from '@common/french';
import {jdnDate} from '@common/gregorian'; import {jdnDate} from '@common/gregorian';
import {jdnLongCount} from '@common/longCount'; import {jdnLongCount} from '@common/longCount';
import {useMobileTooltipProps} from '@common/MobileTooltip';
type MonthProps = { type MonthProps = {
year: number; year: number;
@ -39,10 +40,11 @@ function DayDetail({jdn}: { jdn: number }): JSX.Element {
function NormalDay({year, month, day, todayJDN}: DateProps & { todayJDN: number }): JSX.Element { function NormalDay({year, month, day, todayJDN}: DateProps & { todayJDN: number }): JSX.Element {
const jdn = frJDN(year, month, day); const jdn = frJDN(year, month, day);
const rural = dateRuralName(month, day)!; const rural = dateRuralName(month, day)!;
const mobile = useMobileTooltipProps();
return <div className={`Day NormalDay ${jdn === todayJDN ? 'Day-today' : ''}`}> return <div className={`Day NormalDay ${jdn === todayJDN ? 'Day-today' : ''}`}>
<div className="Day-name">{day}</div> <div className="Day-name">{day}</div>
<div className="Day-decade">{decadeNames[(day - 1) % 10]}</div> <div className="Day-decade">{decadeNames[(day - 1) % 10]}</div>
<div className="Day-rural" title={rural.title} tabIndex={0}>{rural.name}</div> <div className="Day-rural"><abbr title={rural.title} {...mobile}>{rural.name}</abbr></div>
<DayDetail jdn={jdn}/> <DayDetail jdn={jdn}/>
</div>; </div>;
} }

View file

@ -9,6 +9,7 @@
@import 'bootstrap/scss/forms'; @import 'bootstrap/scss/forms';
@import 'bootstrap/scss/root'; @import 'bootstrap/scss/root';
@import './consts'; @import './consts';
@import '@common/MobileTooltip.scss';
body { body {
padding-top: $spacer * 4; padding-top: $spacer * 4;

View file

@ -4,12 +4,15 @@ import 'bootstrap/js/dist/collapse';
import './index.scss'; import './index.scss';
import App from './App'; import App from './App';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
import {MobileTooltipProvider} from '@common/MobileTooltip';
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<App /> <MobileTooltipProvider>
<App/>
</MobileTooltipProvider>
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById('root'),
); );
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function

View file

@ -167,24 +167,3 @@
.Calendar-today-button { .Calendar-today-button {
max-width: 5em; max-width: 5em;
} }
@media (pointer: coarse), (hover: none) {
.DayDetail abbr {
position: relative;
display: inline-flex;
justify-content: center;
&:focus::after {
content: attr(title);
position: absolute;
top: 90%;
left: 0;
color: #000;
background-color: #fff;
border: 1px solid;
width: fit-content;
padding: 3px;
z-index: 10;
}
}
}

View file

@ -4,6 +4,7 @@ import {formatJG, jdnGregorian, JulianDay, JulianMonth, monthName, weekdayNames}
import {jdnLongCount} from '@common/longCount'; import {jdnLongCount} from '@common/longCount';
import {jdnJulian, julianJDN, julianMonthDays} from '@common/julian'; import {jdnJulian, julianJDN, julianMonthDays} from '@common/julian';
import {frDateFormat, frEndJD, frStartJD, jdnFrench} from '@common/french'; import {frDateFormat, frEndJD, frStartJD, jdnFrench} from '@common/french';
import {useMobileTooltipProps} from '@common/MobileTooltip';
type MonthProps = { type MonthProps = {
year: number; year: number;
@ -20,18 +21,19 @@ function WeekdayName({name}: { name: string }): JSX.Element {
function DayDetail({jdn}: { jdn: number }): JSX.Element { function DayDetail({jdn}: { jdn: number }): JSX.Element {
const lc = jdnLongCount(jdn); const lc = jdnLongCount(jdn);
const mobile = useMobileTooltipProps();
return <div className="DayDetail"> return <div className="DayDetail">
<div className="DayDetail-jdn"><abbr title="Julian day number" tabIndex={0}>JD</abbr> {jdn}</div> <div className="DayDetail-jdn"><abbr title="Julian day number" {...mobile}>JD</abbr> {jdn}</div>
<div className="DayDetail-gregorian"> <div className="DayDetail-gregorian">
<abbr title="Gregorian date" tabIndex={0}>G.</abbr>{' '} <abbr title="Gregorian date" {...mobile}>G.</abbr>{' '}
{formatJG(jdnGregorian(jdn))} {formatJG(jdnGregorian(jdn))}
</div> </div>
{lc && <div className="DayDetail-lc"> {lc && <div className="DayDetail-lc">
<abbr title="Mesoamerican long count date" tabIndex={0}>LC</abbr>{' '} <abbr title="Mesoamerican long count date" {...mobile}>LC</abbr>{' '}
{lc.join('.\u200b')} {lc.join('.\u200b')}
</div>} </div>}
{jdn >= frStartJD && jdn <= frEndJD && <div className="DayDetail-fr"> {jdn >= frStartJD && jdn <= frEndJD && <div className="DayDetail-fr">
<abbr title="French Republican Calendar" tabIndex={0}>FR</abbr>{' '} <abbr title="French Republican Calendar" {...mobile}>FR</abbr>{' '}
{frDateFormat(jdnFrench(jdn))} {frDateFormat(jdnFrench(jdn))}
</div>} </div>}
</div>; </div>;

View file

@ -9,6 +9,7 @@
@import 'bootstrap/scss/forms'; @import 'bootstrap/scss/forms';
@import 'bootstrap/scss/root'; @import 'bootstrap/scss/root';
@import './consts'; @import './consts';
@import '@common/MobileTooltip.scss';
body { body {
padding-top: $spacer * 4; padding-top: $spacer * 4;

View file

@ -4,12 +4,15 @@ import 'bootstrap/js/dist/collapse';
import './index.scss'; import './index.scss';
import App from './App'; import App from './App';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
import {MobileTooltipProvider} from '@common/MobileTooltip';
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<App /> <MobileTooltipProvider>
<App/>
</MobileTooltipProvider>
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById('root'),
); );
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function