react-use#useWindowSize JavaScript Examples
The following examples show how to use
react-use#useWindowSize.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.js From ncovis-2020 with MIT License | 5 votes |
function Overview({ overview, getOverview }) {
const { height } = useWindowSize();
const [tab, setTab] = useState("全球");
const { data, time } = overview;
const formate = (time) => {
const date = new Date(time);
return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`;
};
const colorByName = {
累计确诊: "#F9345e",
累计死亡: "#6236ff",
累计治愈: "#1cb142",
现存确诊: "#fa6400",
};
useEffect(() => {
getOverview();
}, [getOverview]);
return (
<Container height={height}>
<An id="overview" />
<DashBoard>
<Title>COVID-19 舆论新闻可视化</Title>
<Tabs
onChange={setTab}
activeKey={tab}
style={{ width: 500, maxWidth: "100%" }}
>
{data.map((d) => (
<TabPane tab={d.name} key={d.name}>
<Box>
<CardContainer>
{d.data.map(({ name, value, change }) => (
<Card key={name}>
<Change color={colorByName[name]}>
{change >= 0 ? "新增" : "减少"}
<span>
{change >= 0 ? "+" : "-"}
{Math.abs(change)}
</span>
</Change>
<Value color={colorByName[name]}>{value}</Value>
<Name>{d.name + name}</Name>
</Card>
))}
</CardContainer>
<Time>{`截至${formate(time)}, ${
d.name === "全国" ? "全国累计(含港澳台地区)" : "全球数据"
}`}</Time>
</Box>
</TabPane>
))}
</Tabs>
</DashBoard>
<Row>
<Zhihu src={zhihuURL} />
<China src={chinaURL} />
</Row>
</Container>
);
}
Example #2
Source File: Player.jsx From xetera.dev with MIT License | 4 votes |
export function Player() {
const token = useRef()
const win = useWindowSize(900)
const [authorized, setAuthorized] = useState(
() => Cookie.get(SPOTIFY_STATUS) === "true"
)
const [volume, setVolume] = useLocalStorage("spotifyVolume", 0.3)
const data = useStaticQuery(query)
const getOAuthToken = useCallback(callback => {
const tokenStr = Cookie.get(SPOTIFY_STATUS)
if (!tokenStr) {
return
}
if (token.current) {
return callback(token.current)
}
refreshToken()
.then(a => {
callback(a)
})
.catch(() => {
Cookie.remove(SPOTIFY_STATUS)
})
}, [])
if (typeof window !== "undefined" && win.width < 990) {
return null
}
function logout() {
token.current = undefined
Cookie.remove(SPOTIFY_STATUS)
setAuthorized(false)
}
async function refresh() {
return fetch("/.netlify/functions/spotify", {
method: "PUT",
}).then(r => r.json())
}
async function refreshToken() {
return refresh().then(result => {
if (result.error) {
// TODO: error handling
return
}
token.current = result.token
setAuthorized(true)
return result.token
})
}
async function play(deviceId, tracks, offset) {
return await fetch("https://api.spotify.com/v1/me/player/play", {
method: "PUT",
body: JSON.stringify({
device_id: deviceId,
uris: tracks,
offset: {
position: offset,
},
}),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token.current}`,
},
})
}
function login() {
const params = new URLSearchParams({
response_type: "code",
client_id: clientId,
scope: SPOTIFY_SCOPES.join(" "),
redirect_uri: redirectUri,
state: window.location,
})
window.location = `https://accounts.spotify.com/authorize?${params}`
}
return (
<WebPlaybackSDK
// we want this to re-render with all of its children
// if the key ever changes
key={authorized}
deviceName="xetera.dev"
getOAuthToken={getOAuthToken}
connectOnInitialized={true}
volume={volume}
>
<PlayerControls
volume={volume}
setVolume={setVolume}
token={token}
refreshToken={refreshToken}
trackList={data.trackList}
authorized={authorized}
logout={logout}
play={play}
login={login}
/>
</WebPlaybackSDK>
)
}
Example #3
Source File: navbar.js From covid19Nepal-react with MIT License | 4 votes |
function Navbar({pages, darkMode, setDarkMode}) {
const [expand, setExpand] = useState(false);
// eslint-disable-next-line
const [isThemeSet, setIsThemeSet] = useLocalStorage('isThemeSet', false);
useLockBodyScroll(expand);
const windowSize = useWindowSize();
return (
<div className="Navbar">
<div
className="navbar-left"
onClick={() => {
setDarkMode((prevMode) => !prevMode);
setIsThemeSet(true);
}}
>
{darkMode ? <Icon.Sun color={'#ffc107'} /> : <Icon.Moon />}
</div>
<div className="navbar-middle">
<Link
to="/"
onClick={() => {
setExpand(false);
}}
>
Nepal <span>Covid19</span>
</Link>
</div>
<div
className="navbar-right"
onClick={() => {
setExpand(!expand);
}}
onMouseEnter={() => {
if (typeof window !== 'undefined' && window.innerWidth > 769) {
setExpand(true);
anime({
targets: '.navbar-right path',
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutSine',
duration: 250,
delay: function (el, i) {
return i * 250;
},
direction: 'alternate',
loop: false,
});
}
}}
>
{typeof window !== 'undefined' && windowSize.width < 769 && (
<span>{expand ? 'Close' : 'Menu'}</span>
)}
{typeof window !== 'undefined' && windowSize.width > 769 && (
<React.Fragment>
<span>
<Link to="/">
<Icon.Home {...activeNavIcon('/')} />
</Link>
</span>
<span>
<Link to="/demographics">
<Icon.Users {...activeNavIcon('/demographics')} />
</Link>
</span>
<span>
<Link to="/deepdive">
<Icon.BarChart2 {...activeNavIcon('/deepdive')} />
</Link>
</span>
<span>
<Link to="/essentials">
<Icon.Package {...activeNavIcon('/essentials')} />
</Link>
</span>
<span>
<Link to="/faq">
<Icon.HelpCircle {...activeNavIcon('/faq')} />
</Link>
</span>
</React.Fragment>
)}
</div>
{expand && <Expand expand={expand} pages={pages} setExpand={setExpand} />}
</div>
);
}
Example #4
Source File: Home.js From covid19india-react with MIT License | 4 votes |
function Home() {
const [regionHighlighted, setRegionHighlighted] = useState({
stateCode: 'TT',
districtName: null,
});
const [anchor, setAnchor] = useLocalStorage('anchor', null);
const [expandTable, setExpandTable] = useLocalStorage('expandTable', false);
const [mapStatistic, setMapStatistic] = useSessionStorage(
'mapStatistic',
'active'
);
const [mapView, setMapView] = useLocalStorage('mapView', MAP_VIEWS.DISTRICTS);
const [date, setDate] = useState('');
const location = useLocation();
const {data: timeseries} = useStickySWR(
`${DATA_API_ROOT}/timeseries.min.json`,
fetcher,
{
revalidateOnMount: true,
refreshInterval: API_REFRESH_INTERVAL,
}
);
const {data} = useStickySWR(
`${DATA_API_ROOT}/data${date ? `-${date}` : ''}.min.json`,
fetcher,
{
revalidateOnMount: true,
refreshInterval: API_REFRESH_INTERVAL,
}
);
const homeRightElement = useRef();
const isVisible = useIsVisible(homeRightElement);
const {width} = useWindowSize();
const hideDistrictData = date !== '' && date < DISTRICT_START_DATE;
const hideDistrictTestData =
date === '' ||
date >
formatISO(
addDays(parseIndiaDate(DISTRICT_TEST_END_DATE), TESTED_EXPIRING_DAYS),
{representation: 'date'}
);
const hideVaccinated =
getStatistic(data?.['TT'], 'total', 'vaccinated') === 0;
const lastDataDate = useMemo(() => {
const updatedDates = [
data?.['TT']?.meta?.date,
data?.['TT']?.meta?.tested?.date,
data?.['TT']?.meta?.vaccinated?.date,
].filter((date) => date);
return updatedDates.length > 0
? formatISO(max(updatedDates.map((date) => parseIndiaDate(date))), {
representation: 'date',
})
: null;
}, [data]);
const lastUpdatedDate = useMemo(() => {
const updatedDates = Object.keys(data || {})
.map((stateCode) => data?.[stateCode]?.meta?.['last_updated'])
.filter((datetime) => datetime);
return updatedDates.length > 0
? formatDateObjIndia(
max(updatedDates.map((datetime) => parseIndiaDate(datetime)))
)
: null;
}, [data]);
const noDistrictDataStates = useMemo(
() =>
// Heuristic: All cases are in Unknown
Object.entries(data || {}).reduce((res, [stateCode, stateData]) => {
res[stateCode] = !!(
stateData?.districts &&
stateData.districts?.[UNKNOWN_DISTRICT_KEY] &&
PRIMARY_STATISTICS.every(
(statistic) =>
getStatistic(stateData, 'total', statistic) ===
getStatistic(
stateData.districts[UNKNOWN_DISTRICT_KEY],
'total',
statistic
)
)
);
return res;
}, {}),
[data]
);
const noRegionHighlightedDistrictData =
regionHighlighted?.stateCode &&
regionHighlighted?.districtName &&
regionHighlighted.districtName !== UNKNOWN_DISTRICT_KEY &&
noDistrictDataStates[regionHighlighted.stateCode];
return (
<>
<Helmet>
<title>Coronavirus Outbreak in India - covid19india.org</title>
<meta
name="title"
content="Coronavirus Outbreak in India: Latest Map and Case Count"
/>
</Helmet>
<div className="Home">
<div className={classnames('home-left', {expanded: expandTable})}>
<div className="header">
<Suspense fallback={<div />}>
<Search />
</Suspense>
{!data && !timeseries && <div style={{height: '60rem'}} />}
<>
{!timeseries && <div style={{minHeight: '61px'}} />}
{timeseries && (
<Suspense fallback={<div style={{minHeight: '61px'}} />}>
<Actions
{...{
date,
setDate,
dates: Object.keys(timeseries['TT']?.dates),
lastUpdatedDate,
}}
/>
</Suspense>
)}
</>
</div>
<div style={{position: 'relative', marginTop: '1rem'}}>
{data && (
<Suspense fallback={<div style={{height: '50rem'}} />}>
{width >= 769 && !expandTable && (
<MapSwitcher {...{mapStatistic, setMapStatistic}} />
)}
<Level data={data['TT']} />
</Suspense>
)}
<>
{!timeseries && <div style={{height: '123px'}} />}
{timeseries && (
<Suspense fallback={<div style={{height: '123px'}} />}>
<Minigraphs
timeseries={timeseries['TT']?.dates}
{...{date}}
/>
</Suspense>
)}
</>
</div>
{!hideVaccinated && <VaccinationHeader data={data['TT']} />}
{data && (
<Suspense fallback={<TableLoader />}>
<Table
{...{
data,
regionHighlighted,
setRegionHighlighted,
expandTable,
setExpandTable,
hideDistrictData,
hideDistrictTestData,
hideVaccinated,
lastDataDate,
noDistrictDataStates,
}}
/>
</Suspense>
)}
</div>
<div
className={classnames('home-right', {expanded: expandTable})}
ref={homeRightElement}
style={{minHeight: '4rem'}}
>
{(isVisible || location.hash) && (
<>
{data && (
<div
className={classnames('map-container', {
expanded: expandTable,
stickied:
anchor === 'mapexplorer' || (expandTable && width >= 769),
})}
>
<Suspense fallback={<div style={{height: '50rem'}} />}>
<StateHeader data={data['TT']} stateCode={'TT'} />
<MapExplorer
{...{
stateCode: 'TT',
data,
mapStatistic,
setMapStatistic,
mapView,
setMapView,
regionHighlighted,
setRegionHighlighted,
anchor,
setAnchor,
expandTable,
lastDataDate,
hideDistrictData,
hideDistrictTestData,
hideVaccinated,
noRegionHighlightedDistrictData,
}}
/>
</Suspense>
</div>
)}
{timeseries && (
<Suspense fallback={<div style={{height: '50rem'}} />}>
<TimeseriesExplorer
stateCode="TT"
{...{
timeseries,
date,
regionHighlighted,
setRegionHighlighted,
anchor,
setAnchor,
expandTable,
hideVaccinated,
noRegionHighlightedDistrictData,
}}
/>
</Suspense>
)}
</>
)}
</div>
</div>
{isVisible && (
<Suspense fallback={<div />}>
<Footer />
</Suspense>
)}
</>
);
}
Example #5
Source File: MapExplorer.js From covid19india-react with MIT License | 4 votes |
function MapExplorer({
stateCode: mapCode = 'TT',
data,
mapView = MAP_VIEWS.DISTRICTS,
setMapView,
mapStatistic,
setMapStatistic,
regionHighlighted,
setRegionHighlighted,
noRegionHighlightedDistrictData,
anchor,
setAnchor,
expandTable = false,
lastDataDate,
hideDistrictData = false,
hideDistrictTestData = true,
hideVaccinated = false,
noDistrictData = false,
}) {
const {t} = useTranslation();
const mapExplorerRef = useRef();
const {width} = useWindowSize();
const [isPerLakh, setIsPerLakh] = useSessionStorage('isPerLakhMap', false);
const [delta7Mode, setDelta7Mode] = useSessionStorage('delta7ModeMap', false);
const mapMeta = MAP_META[mapCode];
const mapData =
mapMeta.mapType === MAP_TYPES.COUNTRY ? data : {[mapCode]: data[mapCode]};
const statisticConfig = STATISTIC_CONFIGS[mapStatistic];
const isDistrictView =
mapView === MAP_VIEWS.DISTRICTS &&
(mapMeta.mapType === MAP_TYPES.STATE ||
(!hideDistrictData &&
!(hideDistrictTestData && statisticConfig?.category === 'tested')));
const hoveredRegion = useMemo(() => {
const hoveredData =
(regionHighlighted.districtName
? data[regionHighlighted.stateCode]?.districts?.[
regionHighlighted.districtName
]
: data[regionHighlighted.stateCode]) || {};
return produce(hoveredData, (draft) => {
draft.name =
regionHighlighted.districtName ||
STATE_NAMES[regionHighlighted.stateCode];
});
}, [data, regionHighlighted.stateCode, regionHighlighted.districtName]);
const handlePerLakhClick = useCallback(() => {
const statisticConfig = STATISTIC_CONFIGS[mapStatistic];
if (statisticConfig?.nonLinear || mapStatistic === 'population') {
return;
}
setIsPerLakh((isPerLakh) => !isPerLakh);
}, [mapStatistic, setIsPerLakh]);
const handleDistrictClick = useCallback(() => {
const newMapView =
mapView === MAP_VIEWS.DISTRICTS ? MAP_VIEWS.STATES : MAP_VIEWS.DISTRICTS;
if (newMapView === MAP_VIEWS.STATES) {
setRegionHighlighted({
stateCode: regionHighlighted.stateCode,
districtName: null,
});
}
setMapView(newMapView);
}, [mapView, regionHighlighted.stateCode, setMapView, setRegionHighlighted]);
const history = useHistory();
const panelRef = useRef();
useEffect(() => {
if (history.location.hash === '#MapExplorer') {
panelRef.current.scrollIntoView();
}
}, [history]);
const trail = useMemo(() => {
const styles = [];
[0, 0, 0, 0, 0, 0, 0].map((element, index) => {
styles.push({
animationDelay: `${index * 250}ms`,
});
return null;
});
return styles;
}, []);
const getMapStatistic = useCallback(
(data) => {
const statisticConfig = STATISTIC_CONFIGS[mapStatistic];
const type =
(statisticConfig?.showDelta && delta7Mode) ||
statisticConfig?.onlyDelta7
? 'delta7'
: 'total';
return getStatistic(data, type, mapStatistic, {
expiredDate: lastDataDate,
normalizedByPopulationPer:
isPerLakh && mapStatistic != 'population' ? 'lakh' : null,
canBeNaN: true,
});
},
[mapStatistic, isPerLakh, lastDataDate, delta7Mode]
);
let currentVal = getMapStatistic(hoveredRegion);
if (isNaN(currentVal)) {
currentVal = '-';
}
const spring = useSpring({
total: currentVal,
config: {tension: 250, ...SPRING_CONFIG_NUMBERS},
});
const mapStatistics = useMemo(
() =>
MAP_STATISTICS.filter(
(statistic) =>
!(STATISTIC_CONFIGS[statistic]?.category === 'vaccinated') ||
!hideVaccinated
),
[hideVaccinated]
);
const handleStatisticChange = useCallback(
(direction) => {
const currentIndex = mapStatistics.indexOf(mapStatistic);
const toIndex =
(mapStatistics.length + currentIndex + direction) %
mapStatistics.length;
setMapStatistic(mapStatistics[toIndex]);
},
[mapStatistic, mapStatistics, setMapStatistic]
);
const swipeHandlers = useSwipeable({
onSwipedLeft: handleStatisticChange.bind(this, 1),
onSwipedRight: handleStatisticChange.bind(this, -1),
});
const mapViz = statisticConfig?.mapConfig?.spike
? MAP_VIZS.SPIKE
: isPerLakh ||
statisticConfig?.mapConfig?.colorScale ||
statisticConfig?.nonLinear
? MAP_VIZS.CHOROPLETH
: MAP_VIZS.BUBBLE;
const handleDeltaClick = useCallback(() => {
if (statisticConfig?.showDelta) {
setDelta7Mode((delta7Mode) => !delta7Mode);
}
}, [statisticConfig, setDelta7Mode]);
const stickied = anchor === 'mapexplorer' || (expandTable && width >= 769);
const transformStatistic = useCallback(
(val) =>
statisticConfig?.mapConfig?.transformFn
? statisticConfig.mapConfig.transformFn(val)
: val,
[statisticConfig]
);
const zoneColor = statisticConfig?.mapConfig?.colorScale
? statisticConfig.mapConfig.colorScale(transformStatistic(currentVal))
: '';
return (
<div
className={classnames(
'MapExplorer',
{stickied},
{
hidden:
anchor && anchor !== 'mapexplorer' && (!expandTable || width < 769),
}
)}
>
<div
className={classnames('anchor', 'fadeInUp', {
stickied,
})}
style={{
display: width < 769 || (width >= 769 && expandTable) ? 'none' : '',
}}
onClick={
setAnchor &&
setAnchor.bind(this, anchor === 'mapexplorer' ? null : 'mapexplorer')
}
>
<PinIcon />
</div>
<div className="panel" ref={panelRef}>
<div className="panel-left fadeInUp" style={trail[0]}>
<h2
className={classnames(mapStatistic)}
style={{color: zoneColor || statisticConfig?.color}}
>
{t(hoveredRegion.name)}
{hoveredRegion.name === UNKNOWN_DISTRICT_KEY &&
` [${t(STATE_NAMES[regionHighlighted.stateCode])}]`}
</h2>
{regionHighlighted.stateCode && (
<h1
className={classnames('district', mapStatistic)}
style={{color: zoneColor || statisticConfig?.color}}
>
<animated.div>
{spring.total.to((total) =>
!noRegionHighlightedDistrictData ||
!statisticConfig?.hasPrimary
? formatNumber(total, statisticConfig.format, mapStatistic)
: '-'
)}
</animated.div>
<StatisticDropdown
currentStatistic={mapStatistic}
statistics={mapStatistics}
mapType={mapMeta.mapType}
{...{
isPerLakh,
delta7Mode,
mapStatistic,
setMapStatistic,
hideDistrictTestData,
hideVaccinated,
zoneColor,
}}
/>
</h1>
)}
</div>
<div className={classnames('panel-right', `is-${mapStatistic}`)}>
<div className="switch-type">
<Tooltip message={'Last 7 day values'} hold>
<div
className={classnames('toggle', 'fadeInUp', {
'is-highlighted':
(delta7Mode && statisticConfig?.showDelta) ||
statisticConfig?.onlyDelta7,
disabled: !statisticConfig?.showDelta,
})}
onClick={handleDeltaClick}
style={trail[1]}
>
<Delta7Icon />
</div>
</Tooltip>
<Tooltip message={'Per lakh people'} hold>
<div
className={classnames('toggle', 'fadeInUp', {
'is-highlighted':
!statisticConfig?.nonLinear &&
mapViz === MAP_VIZS.CHOROPLETH,
disabled:
statisticConfig?.nonLinear || mapStatistic === 'population',
})}
onClick={handlePerLakhClick}
style={trail[2]}
>
<PerLakhIcon />
</div>
</Tooltip>
{mapMeta.mapType === MAP_TYPES.COUNTRY && (
<Tooltip message={'Toggle between states/districts'} hold>
<div
className={classnames('toggle', 'boundary fadeInUp', {
'is-highlighted': isDistrictView,
disabled:
hideDistrictData ||
(statisticConfig?.category === 'tested' &&
hideDistrictTestData),
})}
onClick={handleDistrictClick}
style={trail[3]}
>
<OrganizationIcon />
</div>
</Tooltip>
)}
{mapMeta.mapType === MAP_TYPES.STATE && (
<>
<div className="divider" />
<div
className="toggle back fadeInUp"
onClick={() => {
history.push('/#MapExplorer');
}}
style={trail[4]}
>
<ArrowLeftIcon />
</div>
</>
)}
</div>
<div className="switch-statistic fadeInUp" style={trail[5]}>
{mapStatistics.map((statistic) => (
<div
key={statistic}
className={classnames(
'toggle',
'statistic-option',
`is-${statistic}`,
{
'is-highlighted': mapStatistic === statistic,
}
)}
onClick={setMapStatistic.bind(this, statistic)}
>
<DotFillIcon />
</div>
))}
</div>
</div>
</div>
<div
ref={mapExplorerRef}
className="fadeInUp"
style={trail[3]}
{...swipeHandlers}
>
{mapStatistic && (
<Suspense
fallback={
<MapVisualizerLoader
className="map-loader"
{...{
width: mapExplorerRef.current?.clientWidth,
statistic: mapStatistic,
}}
/>
}
>
<MapVisualizer
data={mapData}
statistic={mapStatistic}
{...{
mapCode,
isDistrictView,
mapViz,
regionHighlighted,
setRegionHighlighted,
getMapStatistic,
transformStatistic,
noDistrictData,
}}
></MapVisualizer>
</Suspense>
)}
</div>
</div>
);
}
Example #6
Source File: Navbar.js From covid19india-react with MIT License | 4 votes |
function Navbar({pages, showLanguageSwitcher, setShowLanguageSwitcher}) {
const {i18n, t} = useTranslation();
const currentLanguage = Object.keys(locales).includes(i18n?.language)
? i18n?.language
: i18n?.options?.fallbackLng[0];
const [expand, setExpand] = useState(false);
const darkMode = useDarkMode(false);
useLockBodyScroll(expand);
const windowSize = useWindowSize();
usePageLeave(() => setExpand(false));
const navbarTransition = useTransition(true, {
from: {opacity: 0},
enter: {opacity: 1},
});
const expandTransition = useTransition(expand, {
from: windowSize.width < 769 ? SLIDE_IN_MOBILE : SLIDE_IN,
enter: windowSize.width < 769 ? SLIDE_OUT_MOBILE : SLIDE_OUT,
leave: windowSize.width < 769 ? SLIDE_IN_MOBILE : SLIDE_IN,
config: {mass: 1, tension: 210, friction: 26},
});
const handleMouseEnter = useCallback(() => {
if (windowSize.width >= 769) {
setExpand(true);
}
}, [windowSize.width]);
const handleLanguageSwitcher = useCallback(() => {
if (expand) setExpand(false);
setShowLanguageSwitcher(!showLanguageSwitcher);
}, [expand, showLanguageSwitcher, setExpand, setShowLanguageSwitcher]);
return navbarTransition((style, item) => (
<animated.div className="Navbar" {...{style}}>
<div className="navbar-left" onClick={handleLanguageSwitcher}>
{locales[currentLanguage]}
</div>
<div className="navbar-middle">
<Link to="/" onClick={setExpand.bind(this, false)}>
Covid19<span>India</span>
</Link>
</div>
<div
className="navbar-right"
onMouseEnter={handleMouseEnter}
{...(windowSize.width < 769 && {
onClick: setExpand.bind(this, !expand),
})}
>
{windowSize.width < 769 && (
<span>{expand ? t('Close') : t('Menu')}</span>
)}
{windowSize.width >= 769 && (
<>
<Link to="/">
<span>
<Home {...activeNavIcon('/')} />
</span>
</Link>
<Link to="/blog">
<span>
<Book {...activeNavIcon('/blog')} />
</span>
</Link>
<Link to="/volunteers">
<span>
<Users {...activeNavIcon('/volunteers')} />
</span>
</Link>
<Link to="/about">
<span>
<HelpCircle {...activeNavIcon('/about')} />
</span>
</Link>
<span>
<SunMoon {...{darkMode}} />
</span>
</>
)}
</div>
{expandTransition(
(style, item) =>
item && (
<animated.div {...{style}}>
<Expand {...{pages, setExpand, darkMode, windowSize}} />
</animated.div>
)
)}
</animated.div>
));
}
Example #7
Source File: TimeseriesExplorer.js From covid19india-react with MIT License | 4 votes |
function TimeseriesExplorer({
stateCode,
timeseries,
date: timelineDate,
regionHighlighted,
setRegionHighlighted,
anchor,
setAnchor,
expandTable = false,
hideVaccinated = false,
noRegionHighlightedDistrictData,
}) {
const {t} = useTranslation();
const [lookback, setLookback] = useLocalStorage('timeseriesLookbackDays', 90);
const [chartType, setChartType] = useLocalStorage('chartType', 'delta');
const [isUniform, setIsUniform] = useLocalStorage('isUniform', false);
const [isLog, setIsLog] = useLocalStorage('isLog', false);
const [isMovingAverage, setIsMovingAverage] = useLocalStorage(
'isMovingAverage',
false
);
const stateCodeDateRange = Object.keys(timeseries?.[stateCode]?.dates || {});
const beginningDate =
stateCodeDateRange[0] || timelineDate || getIndiaDateYesterdayISO();
const endDate = min([
stateCodeDateRange[stateCodeDateRange.length - 1],
timelineDate || getIndiaDateYesterdayISO(),
]);
const [brushSelectionEnd, setBrushSelectionEnd] = useState(endDate);
useEffect(() => {
setBrushSelectionEnd(endDate);
}, [endDate]);
const brushSelectionStart =
lookback !== null
? formatISO(subDays(parseIndiaDate(brushSelectionEnd), lookback), {
representation: 'date',
})
: beginningDate;
const explorerElement = useRef();
const isVisible = useIsVisible(explorerElement, {once: true});
const {width} = useWindowSize();
const selectedRegion = useMemo(() => {
if (timeseries?.[regionHighlighted.stateCode]?.districts) {
return {
stateCode: regionHighlighted.stateCode,
districtName: regionHighlighted.districtName,
};
} else {
return {
stateCode: regionHighlighted.stateCode,
districtName: null,
};
}
}, [timeseries, regionHighlighted.stateCode, regionHighlighted.districtName]);
const selectedTimeseries = useMemo(() => {
if (selectedRegion.districtName) {
return timeseries?.[selectedRegion.stateCode]?.districts?.[
selectedRegion.districtName
]?.dates;
} else {
return timeseries?.[selectedRegion.stateCode]?.dates;
}
}, [timeseries, selectedRegion.stateCode, selectedRegion.districtName]);
const regions = useMemo(() => {
const states = Object.keys(timeseries || {})
.filter((code) => code !== stateCode)
.sort((code1, code2) =>
STATE_NAMES[code1].localeCompare(STATE_NAMES[code2])
)
.map((code) => {
return {
stateCode: code,
districtName: null,
};
});
const districts = Object.keys(timeseries || {}).reduce((acc1, code) => {
return [
...acc1,
...Object.keys(timeseries?.[code]?.districts || {}).reduce(
(acc2, districtName) => {
return [
...acc2,
{
stateCode: code,
districtName: districtName,
},
];
},
[]
),
];
}, []);
return [
{
stateCode: stateCode,
districtName: null,
},
...states,
...districts,
];
}, [timeseries, stateCode]);
const dropdownRegions = useMemo(() => {
if (
regions.find(
(region) =>
region.stateCode === regionHighlighted.stateCode &&
region.districtName === regionHighlighted.districtName
)
)
return regions;
return [
...regions,
{
stateCode: regionHighlighted.stateCode,
districtName: regionHighlighted.districtName,
},
];
}, [regionHighlighted.stateCode, regionHighlighted.districtName, regions]);
const dates = useMemo(
() =>
Object.keys(selectedTimeseries || {}).filter((date) => date <= endDate),
[selectedTimeseries, endDate]
);
const brushSelectionDates = useMemo(
() =>
dates.filter(
(date) => brushSelectionStart <= date && date <= brushSelectionEnd
),
[dates, brushSelectionStart, brushSelectionEnd]
);
const handleChange = useCallback(
({target}) => {
setRegionHighlighted(JSON.parse(target.value));
},
[setRegionHighlighted]
);
const resetDropdown = useCallback(() => {
setRegionHighlighted({
stateCode: stateCode,
districtName: null,
});
}, [stateCode, setRegionHighlighted]);
const statistics = useMemo(
() =>
TIMESERIES_STATISTICS.filter(
(statistic) =>
(!(STATISTIC_CONFIGS[statistic]?.category === 'vaccinated') ||
!hideVaccinated) &&
// (chartType === 'total' || statistic !== 'active') &&
(chartType === 'delta' || statistic !== 'tpr')
),
[chartType, hideVaccinated]
);
return (
<div
className={classnames(
'TimeseriesExplorer fadeInUp',
{
stickied: anchor === 'timeseries',
},
{expanded: expandTable}
)}
style={{
display:
anchor && anchor !== 'timeseries' && (!expandTable || width < 769)
? 'none'
: '',
}}
ref={explorerElement}
>
<div className="timeseries-header">
<div
className={classnames('anchor', 'fadeInUp', {
stickied: anchor === 'timeseries',
})}
style={{
display: expandTable && width >= 769 ? 'none' : '',
}}
onClick={
setAnchor &&
setAnchor.bind(this, anchor === 'timeseries' ? null : 'timeseries')
}
>
<PinIcon />
</div>
<h1>{t('Spread Trends')}</h1>
<div className="tabs">
{Object.entries(TIMESERIES_CHART_TYPES).map(
([ctype, value], index) => (
<div
className={`tab ${chartType === ctype ? 'focused' : ''}`}
key={ctype}
onClick={setChartType.bind(this, ctype)}
>
<h4>{t(value)}</h4>
</div>
)
)}
</div>
<div className="timeseries-options">
<div className="scale-modes">
<label className="main">{`${t('Scale Modes')}:`}</label>
<div className="timeseries-mode">
<label htmlFor="timeseries-mode">{t('Uniform')}</label>
<input
id="timeseries-mode"
type="checkbox"
className="switch"
checked={isUniform}
aria-label={t('Checked by default to scale uniformly.')}
onChange={setIsUniform.bind(this, !isUniform)}
/>
</div>
<div
className={`timeseries-mode ${
chartType !== 'total' ? 'disabled' : ''
}`}
>
<label htmlFor="timeseries-logmode">{t('Logarithmic')}</label>
<input
id="timeseries-logmode"
type="checkbox"
checked={chartType === 'total' && isLog}
className="switch"
disabled={chartType !== 'total'}
onChange={setIsLog.bind(this, !isLog)}
/>
</div>
</div>
<div
className={`timeseries-mode ${
chartType === 'total' ? 'disabled' : ''
} moving-average`}
>
<label htmlFor="timeseries-moving-average">
{t('7 day Moving Average')}
</label>
<input
id="timeseries-moving-average"
type="checkbox"
checked={chartType === 'delta' && isMovingAverage}
className="switch"
disabled={chartType !== 'delta'}
onChange={setIsMovingAverage.bind(this, !isMovingAverage)}
/>
</div>
</div>
</div>
{dropdownRegions && (
<div className="state-selection">
<div className="dropdown">
<select
value={JSON.stringify(selectedRegion)}
onChange={handleChange}
>
{dropdownRegions
.filter(
(region) =>
STATE_NAMES[region.stateCode] !== region.districtName
)
.map((region) => {
return (
<option
value={JSON.stringify(region)}
key={`${region.stateCode}-${region.districtName}`}
>
{region.districtName
? t(region.districtName)
: t(STATE_NAMES[region.stateCode])}
</option>
);
})}
</select>
</div>
<div className="reset-icon" onClick={resetDropdown}>
<ReplyIcon />
</div>
</div>
)}
{isVisible && (
<Suspense fallback={<TimeseriesLoader />}>
<Timeseries
timeseries={selectedTimeseries}
regionHighlighted={selectedRegion}
dates={brushSelectionDates}
{...{
statistics,
endDate,
chartType,
isUniform,
isLog,
isMovingAverage,
noRegionHighlightedDistrictData,
}}
/>
<TimeseriesBrush
timeseries={selectedTimeseries}
regionHighlighted={selectedRegion}
currentBrushSelection={[brushSelectionStart, brushSelectionEnd]}
animationIndex={statistics.length}
{...{dates, endDate, lookback, setBrushSelectionEnd, setLookback}}
/>
</Suspense>
)}
{!isVisible && <div style={{height: '50rem'}} />}
<div
className="pills fadeInUp"
style={{animationDelay: `${(1 + statistics.length) * 250}ms`}}
>
{TIMESERIES_LOOKBACK_DAYS.map((numDays) => (
<button
key={numDays}
type="button"
className={classnames({
selected: numDays === lookback,
})}
onClick={setLookback.bind(this, numDays)}
>
{numDays !== null ? `${numDays} ${t('days')}` : t('Beginning')}
</button>
))}
</div>
</div>
);
}
Example #8
Source File: App.js From FSE-Planner with MIT License | 4 votes |
function App() {
const [filtersBar, setFiltersBar] = React.useState(false)
const [filters, setFilters] = React.useState({
type: 'Trip-Only',
cargo: ['passengers'],
fromIcao: null,
toIcao: null,
minPax: '',
minKg: '',
maxPax: '',
maxKg: '',
minDist: '',
maxDist: '',
minJobPay: '',
minLegPay: '',
percentPay: '',
direction: ''
});
const [table, setTable] = React.useState(false);
const [updatePopup, setUpdatePopup] = React.useState(false);
const [settingsPopup, setSettingsPopup] = React.useState(false);
const [creditsPopup, setCreditsPopup] = React.useState(false);
const [jobs, setJobs] = React.useState(() => {
return transformJobs(storage.get('jobs', {}, true));
});
const [planes, setPlanes] = React.useState(() => {
return transformPlanes(storage.get('planes', {}));
});
const [flight, setFlight] = React.useState(() => {
return transformJobs(storage.get('flight', {}));
});
const [settings, setSettings] = React.useState(() => {
const obj = {};
// Cannot use _defaultsDeep, because it messes up with array
[defaultSettings, storage.get('settings', {})].forEach(item => {
_mergeWith(obj, item, (objectValue, sourceValue) => {
return _isArray(sourceValue) ? sourceValue : undefined;
});
});
return obj;
});
const [search, setSearch] = React.useState(null);
const [searchDest, setSearchDest] = React.useState(null);
const [searchInput, setSearchInput] = React.useState('');
const [searchHistory, setSearchHistory] = React.useState(storage.get('searchHistory', []));
const [icaodata, setIcaodata] = React.useState(icaodataSrc);
const [isTourOpen, setIsTourOpen] = React.useState(storage.get('tutorial') === null);
const [routeFinder, setRouteFinder] = React.useState(false);
const [route, setRoute] = React.useState(null);
const [searchOptions, setSearchOptions] = React.useState([]);
const mapRef = React.useRef();
const searchDestRef = React.useRef(null);
const orientation = useOrientation();
const windowSize = useWindowSize();
const options = React.useMemo(() => ({
jobs: jobs,
planes: planes,
flight: flight,
settings: settings,
icaodata: icaodata,
...filters
}), [jobs, planes, flight, settings, icaodata, filters]);
React.useEffect(() => {
const obj = _clone(icaodataSrc);
icaos.forEach((icao) => {
const nb = wrap(obj[icao].lon, settings.display.map.center);
obj[icao].lon += nb;
});
setIcaodata(obj);
}, [settings.display.map.center]);
React.useEffect(() => {
// Display changelog if new version
const last = storage.get('tutorial');
if (last && last !== process.env.REACT_APP_VERSION) {
setCreditsPopup(true);
storage.set('tutorial', process.env.REACT_APP_VERSION);
}
// Set search if query string in URL
const urlParams = new URLSearchParams(window.location.search);
const icao = urlParams.get('icao');
if (icao) {
setSearch(icao);
}
// Register error logging
window.onerror = (message, file, line, column, errorObject) => {
// We do not use ErrorBoundary component for logging, because it does
// not detect errors in event handlers, and we need to log these errors too
log.error(message, {
file: file,
line: line,
column: column,
obj: errorObject,
});
}
}, []);
// Create goTo function, to allow panning to given ICAO
// Cannot just use setSearch, because need to pan even if the ICAO was already in search var
const goToRef = React.useRef(null);
const goTo = React.useCallback((icao, from = null) => {
if (icao) {
if (from) {
searchDestRef.current = {...icaodataSrc[icao], from: from};
setSearchDest(icao);
setSearch(from);
setSearchOptions([searchDestRef.current]);
setSearchHistory(prevList => {
const list = [...(new Set([icao, from, ...prevList]))].slice(0, 5);
storage.set('searchHistory', list);
return list;
});
window.history.replaceState({icao:from}, '', '?icao='+from);
}
else {
setSearch(prevIcao => prevIcao === icao ? null : icao);
setSearchDest(null);
setSearchOptions([icaodataSrc[icao]]);
goToRef.current = icao;
setSearchHistory(prevList => {
const list = [...(new Set([icao, ...prevList]))].slice(0, 5);
storage.set('searchHistory', list);
return list;
});
window.history.replaceState({icao:icao}, '', '?icao='+icao);
}
}
else {
setSearch(null);
window.history.replaceState(null, '', '?');
}
}, []);
React.useEffect(() => {
if (goToRef.current) {
const icao = goToRef.current;
goToRef.current = null;
setSearch(icao);
}
}, [search]);
// Invalidate map size when routing toogled
React.useEffect(() => {
if (!mapRef.current) { return; }
if (window.scrollY !== 0) {
window.scrollTo(0, 0);
}
mapRef.current.invalidateSize({pan:false});
}, [routeFinder, filters, orientation, table, windowSize.width, windowSize.height]);
// Actions
const actions = React.useRef(null);
const setActions = () => {
actions.current = {
goTo: goTo,
fromIcao: (icao) => setFilters(prev => {
const f = {...prev};
f.fromIcao = icao;
return f;
}),
isFromIcao: (icao) => filters.fromIcao === icao,
isToIcao: (icao) => filters.toIcao === icao,
toIcao: (icao) => setFilters(prev => {
const f = {...prev};
f.toIcao = icao;
return f;
}),
contextMenu: (actions.current && actions.current.contextMenu) ? actions.current.contextMenu : undefined,
measureDistance: () => null,
openTable: () => { setRouteFinder(false); setTable(true); },
getCustomLayers: (icao) => [],
addToCustomLayer: () => null,
removeFromCustomLayer: () => null
};
}
if (!actions.current) { setActions(); }
React.useEffect(setActions, [goTo, filters.fromIcao, filters.toIcao]);
React.useEffect(() => {
const inputs = searchInput.split(/\s*[><]+\s*/g);
// Should not be more than 2 ICAOS long
if (inputs.length > 2) { return setSearchOptions([]); }
// If typing a second ICAO, the first one should be valid
if (inputs.length > 1 && !icaodataSrc.hasOwnProperty(inputs[0])) { return setSearchOptions([]); }
const input = inputs[inputs.length-1];
let filtered = [];
// If input is empty and search history is not, display search history
if (!input && searchHistory.length > 0) {
filtered = searchHistory.map(icao => icaodataSrc[icao]);
}
else {
// Search for ICAO
filtered = filter(icaodataSrcArr, { inputValue: input, getOptionLabel: (a) => a.icao });
// If not enough results, search for city name
if (filtered.length < 5) {
const add = filter(icaodataSrcArr, { inputValue: input, getOptionLabel: (a) => a.name });
filtered = filtered.concat(add.slice(0, 5-filtered.length));
}
}
if (inputs.length === 2) {
filtered = filtered.map(elm => { return {...elm, from: inputs[0]} });
}
if (search) {
if (searchDest) {
const exist = filtered.reduce((acc, elm) => acc || (elm.icao === searchDest && elm.from === search), false);
if (!exist) { filtered.push({...icaodataSrc[searchDest], from: search}) }
}
else {
const exist = filtered.reduce((acc, elm) => acc || (elm.icao === search && !elm.from), false);
if (!exist) { filtered.push(icaodataSrc[search]) }
}
}
setSearchOptions(filtered);
}, [searchInput, searchHistory, search, searchDest]);
return (
<Box
sx={{
display: "flex",
flexFlow: "column",
height: "100%"
}}
>
<AppBar position="static">
<Toolbar>
<Box
sx={{
display: 'flex',
alignItems: 'flex-end',
flexWrap: 'wrap'
}}
>
<Typography variant="h6" sx={{ lineHeight: 1, py: 0.4 }}>
FSE Planner
</Typography>
<Tooltip title="Changelog & credits">
<Button
sx={{
marginLeft: 1,
paddingLeft: '2px',
paddingRight: '2px',
px: 0.5,
py: 0,
fontWeight: 'normal',
color: '#fff',
letterSpacing: 'normal',
textTransform: 'none',
minWidth: 'auto'
}}
onClick={() => setCreditsPopup(true)}
data-tour="Step10"
size="small"
>
v{process.env.REACT_APP_VERSION}
</Button>
</Tooltip>
</Box>
<Box
sx={{
display: "flex",
flexWrap: "wrap",
ml: 1,
flexGrow: 2
}}
>
<Button
sx={styles.menuBtn}
onClick={() => setUpdatePopup(true)}
data-tour="Step2"
startIcon={<UpdateIcon />}
>
Load data
</Button>
<Button
sx={styles.menuBtn}
onClick={() => {
if (!table) {
setRouteFinder(false);
}
setTable(!table);
}}
startIcon={table ? <MapIcon /> : <TableViewIcon />}
>
{table ? "Map" : "Table" }
</Button>
<Button
sx={{
...styles.menuBtn,
...(filtersBar && {
backgroundColor: 'rgba(255, 255, 255, 0.3)',
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.3)',
}
})
}}
onClick={() => setFiltersBar(!filtersBar)}
data-tour="Step7"
startIcon={<FilterAltIcon />}
>
Filters
</Button>
<Button
sx={{
...styles.menuBtn,
...(routeFinder && {
backgroundColor: 'rgba(255, 255, 255, 0.3)',
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.3)',
}
})
}}
onClick={() => {
if (table) {
setTable(false);
setRouteFinder(true);
}
else {
setRouteFinder(!routeFinder)
}
}}
data-tour="Step9"
startIcon={<DirectionsIcon />}
>
Route finder
</Button>
<Button
sx={styles.menuBtn}
onClick={() => setSettingsPopup(true)}
startIcon={<TuneIcon />}
>
Settings
</Button>
</Box>
<Box
sx={{
display: 'flex',
ml: 2
}}
>
<Autocomplete
options={searchOptions}
getOptionLabel={(a) => a.icao ? (a.from ? a.from + ' > ' + a.icao : a.icao) : ''}
renderOption={(props, a) =>
<li {...props}>
<Box
component="span"
sx={{
display: 'flex',
alignItems: 'center',
overflow: 'hidden'
}}
>
{a.from &&
<React.Fragment>
<Box
component="b"
sx={{
minWidth: '40px',
textAlign: 'center'
}}
>
{a.from}
</Box>
<Box
component="span"
sx={{ px: 1}}
>
>
</Box>
</React.Fragment>
}
<Box
component="b"
sx={{
minWidth: '40px',
textAlign: 'center'
}}
>
{a.icao}
</Box>
<Box
component="span"
sx={{
display: 'flex',
flexDirection: 'column',
marginLeft: 2,
overflow: 'hidden',
}}
>
<Box
component="span"
sx={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap'
}}
>
{a.name}
</Box>
<Typography
variant="caption"
sx={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
color: '#aaa'
}}
>
{a.city}, {a.state ? a.state+', ' : ''}{a.country}
</Typography>
</Box>
</Box>
</li>
}
filterOptions={x => x}
renderInput={(params) =>
<InputBase
placeholder="Search..."
sx={styles.inputSearch}
ref={params.InputProps.ref}
inputProps={params.inputProps}
endAdornment={params.inputProps.value ?
<IconButton
size="small"
onClick={() => {
setSearchDest(null);
setSearch(null);
setSearchInput('');
window.history.replaceState(null, '', '?');
}}
>
<CloseIcon />
</IconButton>
:
null
}
/>
}
isOptionEqualToValue={(option, value) => option.icao === value.icao && option.from === value.from}
PopperComponent={PopperMy}
onChange={(evt, value) => value && goTo(value.icao, value.from)}
value={search ? (searchDest ? searchDestRef.current : icaodataSrc[search]) : null}
inputValue={searchInput}
onInputChange={(evt, value) => setSearchInput(value)}
autoHighlight={true}
selectOnFocus={false}
/>
</Box>
</Toolbar>
{filtersBar &&
<Filters
filters={filters}
setFilters={setFilters}
icaodata={icaodata}
actions={actions}
setSettingsPopup={setSettingsPopup}
/>
}
</AppBar>
<Box
sx={{
display: 'flex',
flexWrap: 'nowrap',
flexGrow: '1',
overflow: 'hidden'
}}
>
<Routing
options={options}
setRoute={setRoute}
hidden={!routeFinder}
mapRef={mapRef}
close={() => setRouteFinder(false)}
actions={actions}
/>
<FSEMap
options={options}
search={search}
searchDest={searchDest}
icaos={icaos}
route={route}
mapRef={mapRef}
actions={actions}
hidden={table}
/>
<Table
options={options}
hidden={!table}
actions={actions}
search={search}
searchDest={searchDest}
/>
</Box>
<UpdatePopup
open={updatePopup}
setUpdatePopup={setUpdatePopup}
setJobs={(jobs) => setJobs(transformJobs(jobs))}
setPlanes={(planes) => setPlanes(transformPlanes(planes))}
setFlight={(flight) => setFlight(transformJobs(flight))}
icaodata={icaodata}
icaos={icaos}
settings={settings}
/>
<SettingsPopup
open={settingsPopup}
handleClose={() => setSettingsPopup(false)}
settings={settings}
setSettings={setSettings}
defaultSettings={defaultSettings}
/>
<CreditsPopup
open={creditsPopup}
handleClose={() => setCreditsPopup(false)}
openTutorial={() => setIsTourOpen(true)}
/>
<Tour
isTourOpen={isTourOpen}
setIsTourOpen={setIsTourOpen}
updatePopup={updatePopup}
setUpdatePopup={setUpdatePopup}
/>
</Box>
);
}
Example #9
Source File: index.js From plant-3d-explorer with GNU Affero General Public License v3.0 | 4 votes |
export default function Carousel () {
const canvasRef = useRef(null)
const containerRef = useRef(null)
const windowSider = useWindowSize()
const [scan] = useScan()
const [urlList, setUrlList] = useState([])
const [context, setContext] = useState(null)
const [dragging, setDragging] = useState(false)
const [picturesLayout, setPicturesLayout] = useState([])
const cameraPoses = ((scan && scan.camera.poses) || [])
const [imgs] = useImgLoader(urlList)
const [hovered, setHovered] = useHoveredCamera()
const [selected, setSelected] = useSelectedcamera()
const large = moduleHeight * (6000 / 4000)
let sizes
const [carousel] = useCarousel()
const hoveredLayout = useRef(null)
const selectedLayout = useRef(null)
const imageSet = useImageSet(carousel.photoSet)
useEffect(
() => {
if (imageSet) {
// Portion of code to get only 'rgb' images in dataset (doesn't work)
// if(carousel.photoSet == 'images'){
// let index = 0
// imageSet.forEach(function(image){
// if(image.path.includes('rgb')){
// //imageSet.splice(index, 1)
// index--
// }
// index++
// })
// }
setUrlList(imageSet.map((d) => d.path))
}
},
[imageSet]
)
useEffect(
() => {
const { width, height } = getSize(containerRef.current)
const context = canvasRef.current.getContext('2d')
scaleCanvas(canvasRef.current, context, width, height)
context.width = width
context.height = height
setContext(context)
},
[windowSider, containerRef.current, canvasRef.current]
)
useEffect(
() => {
if (context) {
const { width, height } = getSize(containerRef.current)
sizes = {
width,
large,
normal: (width / urlList.length),
block: (
(
width - (
(hovered || selected)
? large
: 0
)
) /
(
(hovered || selected)
? urlList.length - 1
: urlList.length
)
)
}
let last = { x: 0, width: 0, normalX: 0, normalWidth: 0 }
hoveredLayout.current = null
selectedLayout.current = null
setPicturesLayout(
cameraPoses.map((d, i) => {
const isSelected = selected && d.id === selected.id
const isHovered = hovered && d.id === hovered.id
const x = last.x + last.width
const width = selected
? isSelected
? sizes.large
: sizes.block
: isHovered
? sizes.large
: sizes.block
const normalX = last.normalX + last.normalWidth
const obj = {
item: {
...d,
photoUri: imageSet ? imageSet[i].path : null,
texture: imageSet ? imageSet[i].texture : null
},
x,
normalX,
width,
normalWidth: sizes.normal,
height,
hovered: isHovered,
selected: isSelected
}
last = obj
if (isHovered) hoveredLayout.current = obj
if (isSelected) selectedLayout.current = obj
return obj
})
)
}
},
[windowSider, context, hovered, selected, urlList, cameraPoses]
)
if (context) {
const { width, height } = getSize(containerRef.current)
context.clearRect(0, 0, width, height)
picturesLayout.forEach((d, i) => {
if (imgs[d.item.photoUri]) {
const imgWidth = imgs[d.item.photoUri].width
const imgHeight = imgs[d.item.photoUri].height
const ratio = imgWidth / large
const sx = (imgWidth / 2) - (d.width * ratio * 0.5)
const sy = 0
context.globalAlpha = (d.hovered || d.selected) ? 1 : 0.5
context.drawImage(
imgs[d.item.photoUri],
sx,
sy,
d.width * ratio,
imgHeight,
d.x,
0,
d.width,
height
)
} else {
context.fillStyle = d.hovered ? 'white' : 'grey'
context.fillRect(
d.x,
0,
d.width,
height
)
context.fillStyle = 'black'
}
})
}
useEffect(
() => {
const handler = (e) => {
setDragging(false)
document.body.style.cursor = null
}
const moveHander = (e) => {
if (dragging && e.movementX !== 0) {
const dX = e.movementX < 0
? e.clientX - (dragging.from - dragging.triggerLeft)
: e.clientX - (dragging.from - (dragging.triggerLeft + dragging.triggerWidth))
const pictureDragged = picturesLayout
.find((d) => d.x <= dX && (d.x + d.width) >= dX)
if (pictureDragged) {
setSelected(pictureDragged.item)
}
}
}
if (dragging) {
window.addEventListener('mouseup', handler)
window.addEventListener('mousemove', moveHander)
} else {
window.removeEventListener('mouseup', handler)
window.removeEventListener('mousemove', moveHander)
}
return () => {
window.removeEventListener('mouseup', handler)
window.removeEventListener('mousemove', moveHander)
}
},
[dragging, picturesLayout, selectedLayout]
)
const eventsFn = {
onMouseMove: (e) => {
const dX = !selectedLayout.current && hoveredLayout.current
? e.movementX < 0
? e.clientX - (hoveredLayout.current.width * 0.5) + hoveredLayout.current.normalWidth
: e.clientX + (hoveredLayout.current.width * 0.5)
: e.clientX
const pictureHovered = picturesLayout
.find((d) => d.x <= dX && (d.x + d.width) >= dX)
if (pictureHovered && hovered !== pictureHovered.item) {
setHovered(pictureHovered ? pictureHovered.item : null)
}
},
onMouseOut: (e) => {
setHovered(null)
},
onClick: () => {
if (hovered) {
setSelected(
(selected && selected.id === hovered.id)
? null
: hovered
)
}
}
}
return <Container ref={containerRef}>
<SVGCartridge
large={large}
hoveredLayout={hoveredLayout.current}
selectedLayout={selectedLayout.current}
eventsFn={eventsFn}
/>
<Canvas ref={canvasRef} />
{
selectedLayout.current && <SvgDnG>
<g transform={`translate(0, ${moduleHeight * 0.5})`}>
<line
x1={0}
x2={'100%'}
y1={0}
y2={0}
strokeWidth={1}
stroke={green}
/>
<g
transform={`translate(${selectedLayout.current.x}, 0)`}
>
<rect
style={{
cursor: !dragging && 'grab'
}}
y={-15}
width={selectedLayout.current.width}
height={30}
rx={15}
ry={15}
fill={green}
onMouseDown={(e) => {
const bb = e.target.getBoundingClientRect()
document.body.style.cursor = 'grabbing'
setDragging({
from: e.clientX,
triggerLeft: bb.left,
triggerWidth: bb.width
})
}}
/>
<image
xlinkHref={dragNdropIco}
x={(large * 0.5) - 20}
y={-8}
/>
</g>
</g>
</SvgDnG>
}
</Container>
}