react-use#useSessionStorage JavaScript Examples

The following examples show how to use react-use#useSessionStorage. 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: authentication.js    From fluentui-starter with MIT License 6 votes vote down vote up
export function AuthenticationProvider({ children }) {
  const [authentication, setAuthentication] = useSessionStorage(
    STOREGE_KEY,
    defaultValues
  );

  const login = (principal) =>
    setAuthentication({ isAuthenticated: true, principal });

  const logout = () => setAuthentication(defaultValues);

  const authenticationValue = { ...authentication, login, logout };

  return (
    <AuthenticationContext.Provider value={authenticationValue}>
      {children}
    </AuthenticationContext.Provider>
  );
}
Example #2
Source File: Home.js    From covid19india-react with MIT License 4 votes vote down vote up
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 #3
Source File: MapExplorer.js    From covid19india-react with MIT License 4 votes vote down vote up
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 #4
Source File: Row.js    From covid19india-react with MIT License 4 votes vote down vote up
function Row({
  data,
  tableStatistics,
  stateCode,
  districtName,
  regionHighlighted,
  setRegionHighlighted,
  expandTable,
  getTableStatistic,
  tableWidth,
  noDistrictData,
}) {
  const [showDistricts, setShowDistricts] = useState(false);
  const [sortData, setSortData] = useSessionStorage('districtSortData', {
    sortColumn: 'confirmed',
    isAscending: false,
    delta: false,
  });

  const history = useHistory();
  const {t} = useTranslation();

  const rowElement = useRef();

  const handleSortClick = useCallback(
    (statistic) => {
      if (sortData.sortColumn !== statistic) {
        setSortData(
          produce(sortData, (draftSortData) => {
            if (
              sortData.sortColumn === 'districtName' ||
              statistic === 'districtName'
            ) {
              draftSortData.isAscending = !sortData.isAscending;
            }
            draftSortData.sortColumn = statistic;
          })
        );
      } else {
        setSortData(
          produce(sortData, (draftSortData) => {
            draftSortData.isAscending = !sortData.isAscending;
          })
        );
      }
    },
    [sortData, setSortData]
  );

  const sortingFunction = useCallback(
    (districtNameA, districtNameB) => {
      if (sortData.sortColumn !== 'districtName') {
        const statisticConfig = STATISTIC_CONFIGS[sortData.sortColumn];
        const dataType =
          sortData.delta && statisticConfig?.showDelta ? 'delta' : 'total';

        const statisticA = getTableStatistic(
          data.districts[districtNameA],
          sortData.sortColumn,
          dataType
        );
        const statisticB = getTableStatistic(
          data.districts[districtNameB],
          sortData.sortColumn,
          dataType
        );
        return sortData.isAscending
          ? statisticA - statisticB
          : statisticB - statisticA;
      } else {
        return sortData.isAscending
          ? districtNameA.localeCompare(districtNameB)
          : districtNameB.localeCompare(districtNameA);
      }
    },
    [sortData, data, getTableStatistic]
  );

  const highlightState = useCallback(() => {
    if (stateCode) {
      if (regionHighlighted.stateCode !== stateCode) {
        setRegionHighlighted(
          produce(regionHighlighted, (draftRegionHighlighted) => {
            draftRegionHighlighted.stateCode = stateCode;
            draftRegionHighlighted.districtName = null;
          })
        );
      }
    } else if (districtName) {
      if (
        regionHighlighted.districtName !== districtName ||
        regionHighlighted.stateCode !== data.stateCode
      ) {
        setRegionHighlighted(
          produce(regionHighlighted, (draftRegionHighlighted) => {
            draftRegionHighlighted.stateCode = data.stateCode;
            draftRegionHighlighted.districtName = districtName;
          })
        );
      }
    }
  }, [
    data.stateCode,
    districtName,
    regionHighlighted,
    setRegionHighlighted,
    stateCode,
  ]);

  const _setShowDistrict = useCallback(() => {
    if (data.districts) {
      setShowDistricts(!showDistricts);
    }
  }, [showDistricts, data]);

  let districtNameStr = t(districtName);
  if (districtName === UNKNOWN_DISTRICT_KEY) {
    districtNameStr = `${t(UNKNOWN_DISTRICT_KEY)} [${t(
      STATE_NAMES[data.stateCode]
    )}]`;
  }

  const handleStatePageClick = useCallback(
    (stateCode) => {
      history.push(`state/${stateCode}`);
    },
    [history]
  );

  const handleCollapse = useCallback(() => {
    setShowDistricts(false);
    rowElement.current.scrollIntoView({
      block: 'start',
    });
  }, []);

  return (
    <>
      <div
        className={classnames(
          'row',
          {'is-total': stateCode === 'TT'},
          {
            'is-highlighted':
              (stateCode && regionHighlighted?.stateCode === stateCode) ||
              (districtName &&
                regionHighlighted?.districtName === districtName &&
                regionHighlighted?.stateCode === data.stateCode),
          }
        )}
        onMouseEnter={highlightState}
        onClick={_setShowDistrict}
        ref={rowElement}
      >
        <div className="cell">
          <div className="state-name fadeInUp">
            {t(STATE_NAMES[stateCode]) || districtNameStr}
          </div>
          {data?.meta?.notes && (
            <Tooltip message={data.meta.notes}>
              <InfoIcon size={16} />
            </Tooltip>
          )}
        </div>

        {tableStatistics.map((statistic) => (
          <Cell
            key={statistic}
            noDistrictData={
              !stateCode &&
              districtName !== UNKNOWN_DISTRICT_KEY &&
              noDistrictData
            }
            {...{
              data,
              statistic,
              getTableStatistic,
            }}
          />
        ))}
      </div>

      {showDistricts && (
        <>
          <div className="state-meta" style={{width: tableWidth}}>
            <div className="state-meta-top">
              <div
                className="state-page"
                onClick={handleStatePageClick.bind(this, stateCode)}
              >
                <GraphIcon />
                <span>{t('See more details')}</span>
              </div>
              {data?.meta?.['last_updated'] && (
                <p className="last-updated">
                  <ClockIcon />
                  {capitalize(
                    `${formatLastUpdated(data.meta.last_updated)} ${t('ago')}`
                  )}
                </p>
              )}
            </div>

            {data.districts && UNKNOWN_DISTRICT_KEY in data.districts && (
              <div className="state-meta-bottom">
                <div className={classnames('disclaimer')}>
                  <AlertIcon />
                  <span>
                    {t('District-wise data not available in state bulletin')}
                  </span>
                </div>
              </div>
            )}
          </div>

          <div className={classnames('row', 'heading')}>
            <div
              className="cell heading"
              onClick={handleSortClick.bind(this, 'districtName')}
            >
              <div className="district-name">{t('District')}</div>
              {sortData.sortColumn === 'districtName' && (
                <div className={'sort-icon'}>
                  {sortData.isAscending ? (
                    <SortAscIcon size={12} />
                  ) : (
                    <SortDescIcon size={12} />
                  )}
                </div>
              )}
            </div>

            {tableStatistics.map((statistic) => (
              <HeaderCell
                key={statistic}
                {...{statistic, sortData, setSortData}}
                handleSort={handleSortClick.bind(this, statistic)}
              />
            ))}
          </div>
        </>
      )}

      {showDistricts &&
        Object.keys(data.districts || {})
          .sort((a, b) => sortingFunction(a, b))
          .map((districtName) => (
            <DistrictRow
              data={data.districts[districtName]}
              key={districtName}
              noDistrictData={
                districtName !== UNKNOWN_DISTRICT_KEY && noDistrictData
              }
              {...{
                tableStatistics,
                districtName,
                regionHighlighted,
                setRegionHighlighted,
                stateCode,
                expandTable,
                getTableStatistic,
              }}
            />
          ))}

      {showDistricts && (
        <div className="spacer-row" style={{width: tableWidth}}>
          <div className="spacer">
            <p>{`End of ${t(STATE_NAMES[stateCode])}'s districts`}</p>
            <div className="fold" onClick={handleCollapse}>
              <FoldUpIcon />
            </div>
          </div>
        </div>
      )}
    </>
  );
}
Example #5
Source File: State.js    From covid19india-react with MIT License 4 votes vote down vote up
function State() {
  const {t} = useTranslation();

  const stateCode = useParams().stateCode.toUpperCase();

  const [mapStatistic, setMapStatistic] = useSessionStorage(
    'mapStatistic',
    'active'
  );
  const [showAllDistricts, setShowAllDistricts] = useState(false);
  const [regionHighlighted, setRegionHighlighted] = useState({
    stateCode: stateCode,
    districtName: null,
  });
  const [delta7Mode, setDelta7Mode] = useState(false);

  useEffect(() => {
    if (regionHighlighted.stateCode !== stateCode) {
      setRegionHighlighted({
        stateCode: stateCode,
        districtName: null,
      });
      setShowAllDistricts(false);
    }
  }, [regionHighlighted.stateCode, stateCode]);

  const {data: timeseries, error: timeseriesResponseError} = useSWR(
    `${DATA_API_ROOT}/timeseries-${stateCode}.min.json`,
    fetcher,
    {
      revalidateOnMount: true,
      refreshInterval: 100000,
    }
  );

  const {data} = useSWR(`${DATA_API_ROOT}/data.min.json`, fetcher, {
    revalidateOnMount: true,
    refreshInterval: 100000,
  });

  const stateData = data?.[stateCode];

  const toggleShowAllDistricts = () => {
    setShowAllDistricts(!showAllDistricts);
  };

  const handleSort = (districtNameA, districtNameB) => {
    const districtA = stateData.districts[districtNameA];
    const districtB = stateData.districts[districtNameB];
    return (
      getStatistic(districtB, 'total', mapStatistic) -
      getStatistic(districtA, 'total', mapStatistic)
    );
  };

  const gridRowCount = useMemo(() => {
    if (!stateData) return;
    const gridColumnCount = window.innerWidth >= 540 ? 3 : 2;
    const districtCount = stateData?.districts
      ? Object.keys(stateData.districts).filter(
          (districtName) => districtName !== 'Unknown'
        ).length
      : 0;
    const gridRowCount = Math.ceil(districtCount / gridColumnCount);
    return gridRowCount;
  }, [stateData]);

  const stateMetaElement = useRef();
  const isStateMetaVisible = useIsVisible(stateMetaElement);

  const trail = useMemo(() => {
    const styles = [];

    [0, 0, 0, 0].map((element, index) => {
      styles.push({
        animationDelay: `${index * 250}ms`,
      });
      return null;
    });
    return styles;
  }, []);

  const lookback = showAllDistricts ? (window.innerWidth >= 540 ? 10 : 8) : 6;

  const lastDataDate = useMemo(() => {
    const updatedDates = [
      stateData?.meta?.date,
      stateData?.meta?.tested?.date,
      stateData?.meta?.vaccinated?.date,
    ].filter((date) => date);
    return updatedDates.length > 0
      ? formatISO(max(updatedDates.map((date) => parseIndiaDate(date))), {
          representation: 'date',
        })
      : null;
  }, [stateData]);

  const primaryStatistic = MAP_STATISTICS.includes(mapStatistic)
    ? mapStatistic
    : 'confirmed';

  const noDistrictData = useMemo(() => {
    // Heuristic: All cases are in Unknown
    return !!(
      stateData?.districts &&
      stateData.districts?.[UNKNOWN_DISTRICT_KEY] &&
      PRIMARY_STATISTICS.every(
        (statistic) =>
          getStatistic(stateData, 'total', statistic) ===
          getStatistic(
            stateData.districts[UNKNOWN_DISTRICT_KEY],
            'total',
            statistic
          )
      )
    );
  }, [stateData]);

  const statisticConfig = STATISTIC_CONFIGS[primaryStatistic];

  const noRegionHighlightedDistrictData =
    regionHighlighted?.districtName &&
    regionHighlighted.districtName !== UNKNOWN_DISTRICT_KEY &&
    noDistrictData;

  const districts = Object.keys(
    ((!noDistrictData || !statisticConfig.hasPrimary) &&
      stateData?.districts) ||
      {}
  );

  return (
    <>
      <Helmet>
        <title>
          Coronavirus Outbreak in {STATE_NAMES[stateCode]} - covid19india.org
        </title>
        <meta
          name="title"
          content={`Coronavirus Outbreak in ${STATE_NAMES[stateCode]}: Latest Map and Case Count`}
        />
      </Helmet>

      <div className="State">
        <div className="state-left">
          <StateHeader data={stateData} stateCode={stateCode} />

          <div style={{position: 'relative'}}>
            <MapSwitcher {...{mapStatistic, setMapStatistic}} />
            <Level data={stateData} />
            <Minigraphs
              timeseries={timeseries?.[stateCode]?.dates}
              {...{stateCode}}
              forceRender={!!timeseriesResponseError}
            />
          </div>

          {stateData?.total?.vaccinated1 && (
            <VaccinationHeader data={stateData} />
          )}

          {data && (
            <Suspense fallback={<div style={{minHeight: '50rem'}} />}>
              <MapExplorer
                {...{
                  stateCode,
                  data,
                  regionHighlighted,
                  setRegionHighlighted,
                  mapStatistic,
                  setMapStatistic,
                  lastDataDate,
                  delta7Mode,
                  setDelta7Mode,
                  noRegionHighlightedDistrictData,
                  noDistrictData,
                }}
              ></MapExplorer>
            </Suspense>
          )}

          <span ref={stateMetaElement} />

          {isStateMetaVisible && data && (
            <Suspense fallback={<div />}>
              <StateMeta
                {...{
                  stateCode,
                  data,
                }}
                timeseries={timeseries?.[stateCode]?.dates}
              />
            </Suspense>
          )}
        </div>

        <div className="state-right">
          <>
            <div className="district-bar">
              <div
                className={classnames('district-bar-top', {
                  expanded: showAllDistricts,
                })}
              >
                <div className="district-bar-left">
                  <h2
                    className={classnames(primaryStatistic, 'fadeInUp')}
                    style={trail[0]}
                  >
                    {t('Top districts')}
                  </h2>
                  <div
                    className={`districts fadeInUp ${
                      showAllDistricts ? 'is-grid' : ''
                    }`}
                    style={
                      showAllDistricts
                        ? {
                            gridTemplateRows: `repeat(${gridRowCount}, 2rem)`,
                            ...trail[1],
                          }
                        : trail[1]
                    }
                  >
                    {districts
                      .filter((districtName) => districtName !== 'Unknown')
                      .sort((a, b) => handleSort(a, b))
                      .slice(0, showAllDistricts ? undefined : 5)
                      .map((districtName) => {
                        const total = getStatistic(
                          stateData.districts[districtName],
                          'total',
                          primaryStatistic
                        );
                        const delta = getStatistic(
                          stateData.districts[districtName],
                          'delta',
                          primaryStatistic
                        );
                        return (
                          <div key={districtName} className="district">
                            <h2>{formatNumber(total)}</h2>
                            <h5>{t(districtName)}</h5>
                            {primaryStatistic !== 'active' && (
                              <div className="delta">
                                <h6 className={primaryStatistic}>
                                  {delta > 0
                                    ? '\u2191' + formatNumber(delta)
                                    : ''}
                                </h6>
                              </div>
                            )}
                          </div>
                        );
                      })}
                  </div>
                </div>

                <div className="district-bar-right fadeInUp" style={trail[2]}>
                  {timeseries &&
                    (primaryStatistic === 'confirmed' ||
                      primaryStatistic === 'deceased') && (
                      <div className="happy-sign">
                        {Object.keys(timeseries[stateCode]?.dates || {})
                          .slice(-lookback)
                          .every(
                            (date) =>
                              getStatistic(
                                timeseries[stateCode].dates[date],
                                'delta',
                                primaryStatistic
                              ) === 0
                          ) && (
                          <div
                            className={`alert ${
                              primaryStatistic === 'confirmed' ? 'is-green' : ''
                            }`}
                          >
                            <SmileyIcon />
                            <div className="alert-right">
                              No new {primaryStatistic} cases in the past five
                              days
                            </div>
                          </div>
                        )}
                      </div>
                    )}
                  <DeltaBarGraph
                    timeseries={timeseries?.[stateCode]?.dates}
                    statistic={primaryStatistic}
                    {...{stateCode, lookback}}
                    forceRender={!!timeseriesResponseError}
                  />
                </div>
              </div>

              <div className="district-bar-bottom">
                {districts.length > 5 ? (
                  <button
                    className="button fadeInUp"
                    onClick={toggleShowAllDistricts}
                    style={trail[3]}
                  >
                    <span>
                      {t(showAllDistricts ? 'View less' : 'View all')}
                    </span>
                  </button>
                ) : (
                  <div style={{height: '3.75rem', flexBasis: '15%'}} />
                )}
              </div>
            </div>

            <Suspense fallback={<div />}>
              <TimeseriesExplorer
                {...{
                  stateCode,
                  timeseries,
                  regionHighlighted,
                  setRegionHighlighted,
                  noRegionHighlightedDistrictData,
                }}
                forceRender={!!timeseriesResponseError}
              />
            </Suspense>
          </>
        </div>
      </div>

      <Footer />
    </>
  );
}
Example #6
Source File: Table.js    From covid19india-react with MIT License 4 votes vote down vote up
function Table({
  data: states,
  date: timelineDate,
  regionHighlighted,
  setRegionHighlighted,
  expandTable,
  setExpandTable,
  hideDistrictData,
  hideDistrictTestData,
  hideVaccinated,
  lastDataDate,
  noDistrictDataStates,
}) {
  const {t} = useTranslation();
  const [sortData, setSortData] = useSessionStorage('sortData', {
    sortColumn: 'confirmed',
    isAscending: false,
    delta: false,
  });
  const [page, setPage] = useState(0);
  const [delta7Mode, setDelta7Mode] = useState(false);

  const [tableContainerRef, {width: tableWidth}] = useMeasure();

  const handleSortClick = useCallback(
    (statistic) => {
      if (sortData.sortColumn !== statistic) {
        setSortData(
          produce(sortData, (draftSortData) => {
            if (
              sortData.sortColumn === 'regionName' ||
              statistic === 'regionName'
            ) {
              draftSortData.isAscending = !sortData.isAscending;
            }
            draftSortData.sortColumn = statistic;
          })
        );
      } else {
        setSortData(
          produce(sortData, (draftSortData) => {
            draftSortData.isAscending = !sortData.isAscending;
          })
        );
      }
    },
    [sortData, setSortData]
  );

  const trail = useTrail(5, {
    from: {transform: 'translate3d(0, 10px, 0)', opacity: 0},
    to: {transform: 'translate3d(0, 0px, 0)', opacity: 1},
    config: config.wobbly,
  });

  const [allDistricts, setAllDistricts] = useState();

  const [tableOption, setTableOption] = useState('States');
  const [isPerLakh, setIsPerLakh] = useState(false);
  const [isInfoVisible, setIsInfoVisible] = useState(false);

  const getTableStatistic = useCallback(
    (data, statistic, type) => {
      const statisticConfig = STATISTIC_CONFIGS[statistic];
      if (type == 'total' && statisticConfig?.onlyDelta7) {
        type = 'delta7';
      }

      if (statisticConfig?.showDelta && type === 'total' && delta7Mode) {
        type = 'delta7';
      }

      return getStatistic(data, type, statistic, {
        expiredDate: lastDataDate,
        normalizedByPopulationPer: isPerLakh ? 'lakh' : null,
      });
    },
    [isPerLakh, lastDataDate, delta7Mode]
  );

  const districts = useMemo(() => {
    if (!isPerLakh) {
      return allDistricts;
    } else {
      return Object.keys(allDistricts || {})
        .filter(
          (districtKey) =>
            getStatistic(allDistricts[districtKey], 'total', 'population') > 0
        )
        .reduce((res, districtKey) => {
          res[districtKey] = allDistricts[districtKey];
          return res;
        }, {});
    }
  }, [isPerLakh, allDistricts]);

  const numPages = Math.ceil(
    Object.keys(districts || {}).length / DISTRICT_TABLE_COUNT
  );

  const sortingFunction = useCallback(
    (regionKeyA, regionKeyB) => {
      if (sortData.sortColumn !== 'regionName') {
        const statisticConfig = STATISTIC_CONFIGS[sortData.sortColumn];
        const dataType =
          sortData.delta && statisticConfig?.showDelta ? 'delta' : 'total';

        const statisticA = getTableStatistic(
          districts?.[regionKeyA] || states[regionKeyA],
          sortData.sortColumn,
          dataType
        );
        const statisticB = getTableStatistic(
          districts?.[regionKeyB] || states[regionKeyB],
          sortData.sortColumn,
          dataType
        );
        return sortData.isAscending
          ? statisticA - statisticB
          : statisticB - statisticA;
      } else {
        const regionNameA =
          districts?.[regionKeyA]?.districtName || STATE_NAMES[regionKeyA];
        const regionNameB =
          districts?.[regionKeyB]?.districtName || STATE_NAMES[regionKeyB];
        return sortData.isAscending
          ? regionNameA.localeCompare(regionNameB)
          : regionNameB.localeCompare(regionNameA);
      }
    },
    [
      districts,
      getTableStatistic,
      sortData.delta,
      sortData.isAscending,
      sortData.sortColumn,
      states,
    ]
  );

  const _setTableOption = useCallback(() => {
    setTableOption((prevTableOption) =>
      prevTableOption === 'States' ? 'Districts' : 'States'
    );
  }, []);

  useEffect(() => {
    const workerInstance = worker();
    workerInstance.getDistricts(states);
    workerInstance.addEventListener('message', (message) => {
      if (message.data.type !== 'RPC') {
        setAllDistricts(message.data);
        workerInstance.terminate();
      }
    });
  }, [tableOption, states]);

  useEffect(() => {
    setPage((p) => Math.max(0, Math.min(p, numPages - 1)));
  }, [numPages]);

  const handlePageClick = (direction) => {
    if (Math.abs(direction) === 1) {
      setPage(Math.min(Math.max(0, page + direction), numPages - 1));
    } else if (direction < 0) {
      setPage(0);
    } else if (direction > 0) {
      setPage(numPages - 1);
    }
  };

  const transition = useTransition(isInfoVisible, {
    from: TABLE_FADE_OUT,
    enter: TABLE_FADE_IN,
    leave: TABLE_FADE_OUT,
  });

  const tableStatistics = (
    expandTable ? TABLE_STATISTICS_EXPANDED : TABLE_STATISTICS
  ).filter(
    (statistic) =>
      (tableOption === 'States' ||
        STATISTIC_CONFIGS[statistic]?.category !== 'tested' ||
        !hideDistrictTestData) &&
      (STATISTIC_CONFIGS[statistic]?.category !== 'vaccinated' ||
        !hideVaccinated)
  );

  const showDistricts = tableOption === 'Districts' && !hideDistrictData;

  useEffect(() => {
    if (!showDistricts) {
      setPage(0);
    }
  }, [showDistricts]);

  useKeyPressEvent('?', () => {
    setIsInfoVisible(!isInfoVisible);
  });

  return (
    <div className="Table">
      <div className="table-top">
        <div className="table-top-left">
          <Tooltip message={'Toggle between states/districts'} hold>
            <animated.div
              className={classnames('toggle', 'option-toggle', {
                'is-highlighted': showDistricts,
                disabled: hideDistrictData,
              })}
              onClick={_setTableOption}
              style={trail[0]}
            >
              <DistrictIcon />
            </animated.div>
          </Tooltip>

          <Tooltip message={'Per lakh people'} hold>
            <animated.div
              className={classnames('toggle', 'lakh-toggle', {
                'is-highlighted': isPerLakh,
              })}
              onClick={setIsPerLakh.bind(this, !isPerLakh)}
              style={trail[1]}
            >
              <PerLakhIcon />
            </animated.div>
          </Tooltip>

          <Tooltip message={'Last 7 day values'} hold>
            <animated.div
              className={classnames('toggle', 'delta-toggle', {
                'is-highlighted': delta7Mode,
              })}
              style={trail[2]}
              onClick={setDelta7Mode.bind(this, !delta7Mode)}
            >
              <Delta7Icon />
            </animated.div>
          </Tooltip>

          <animated.div
            className={classnames('toggle', 'info-toggle', {
              'is-highlighted': isInfoVisible,
            })}
            onClick={setIsInfoVisible.bind(this, !isInfoVisible)}
            style={trail[3]}
          >
            <QuestionIcon size={14} />
          </animated.div>
        </div>

        <Tooltip message={`${expandTable ? 'Collapse' : 'Expand'} table`} hold>
          <animated.div
            className={classnames('toggle', 'expand-table-toggle', {
              'is-highlighted': expandTable,
            })}
            style={trail[4]}
            onClick={setExpandTable.bind(this, !expandTable)}
          >
            <FoldDownIcon size={16} />
          </animated.div>
        </Tooltip>
      </div>

      {transition(
        (style, item) =>
          item && (
            <animated.div className="table-helper" {...{style}}>
              <div className="helper-top">
                <div className="helper-left">
                  <div className="info-item">
                    <div>
                      <OrganizationIcon size={14} />
                    </div>
                    <p>{t('Toggle between States/Districts')}</p>
                  </div>

                  <div className="info-item">
                    <div>
                      <PeopleIcon size={16} />
                    </div>
                    <p>{t('Per Lakh People')}</p>
                  </div>

                  <div className="info-item">
                    <div>
                      <PulseIcon size={16} />
                    </div>
                    <p>{t('Last 7 day values')}</p>
                  </div>

                  <div className="info-item sort">
                    <div>
                      <SortDescIcon size={14} />
                    </div>
                    <p>{t('Sorted by Descending')}</p>
                  </div>

                  <div className="info-item sort">
                    <div>
                      <SortAscIcon size={14} />
                    </div>
                    <p>{t('Sorted by Ascending')}</p>
                  </div>

                  <div className="info-item sort">
                    <TableDeltaHelper />
                  </div>

                  <div className="info-item notes">
                    <div>
                      <InfoIcon size={15} />
                    </div>
                    <p>{t('Notes')}</p>
                  </div>
                </div>

                <div className="helper-right">
                  <div className="info-item">
                    <p>{t('Units')}</p>
                  </div>
                  {Object.entries({'1K': 3, '1L': 5, '1Cr': 7}).map(
                    ([abbr, exp]) => (
                      <div className="info-item abbr" key={abbr}>
                        <h5>{abbr}</h5>
                        <p>
                          10
                          <sup>{exp}</sup>
                        </p>
                      </div>
                    )
                  )}
                </div>
              </div>

              <h5 className="text">
                {t('Compiled from State Govt. numbers')},{' '}
                <Link to="/about">{t('know more')}!</Link>
              </h5>
            </animated.div>
          )
      )}

      <div className="table-container" ref={tableContainerRef}>
        <div
          className="table fadeInUp"
          style={{
            gridTemplateColumns: `repeat(${tableStatistics.length + 1}, auto)`,
          }}
        >
          <div className="row heading">
            <div
              className="cell heading"
              onClick={handleSortClick.bind(this, 'regionName')}
            >
              <div>{t(!showDistricts ? 'State/UT' : 'District')}</div>
              {sortData.sortColumn === 'regionName' && (
                <div className={'sort-icon'}>
                  {sortData.isAscending ? (
                    <SortAscIcon size={12} />
                  ) : (
                    <SortDescIcon size={12} />
                  )}
                </div>
              )}
            </div>

            {tableStatistics.map((statistic) => (
              <HeaderCell
                key={statistic}
                {...{
                  statistic,
                  sortData,
                  setSortData,
                }}
                handleSort={handleSortClick.bind(this, statistic)}
              />
            ))}
          </div>

          {!showDistricts &&
            Object.keys(states)
              .filter(
                (stateCode) =>
                  stateCode !== 'TT' &&
                  !(stateCode === UNASSIGNED_STATE_CODE && isPerLakh)
              )
              .sort((a, b) => sortingFunction(a, b))
              .map((stateCode) => {
                return (
                  <Row
                    key={stateCode}
                    data={states[stateCode]}
                    noDistrictData={noDistrictDataStates[stateCode]}
                    {...{
                      stateCode,
                      regionHighlighted,
                      setRegionHighlighted,
                      expandTable,
                      tableStatistics,
                      getTableStatistic,
                      tableWidth,
                    }}
                  />
                );
              })}

          {showDistricts && !districts && <TableLoader />}

          {showDistricts &&
            districts &&
            Object.keys(districts)
              .sort((a, b) => sortingFunction(a, b))
              .slice(
                page * DISTRICT_TABLE_COUNT,
                (page + 1) * DISTRICT_TABLE_COUNT
              )
              .map((districtKey) => {
                const noDistrictData =
                  noDistrictDataStates[districts[districtKey].stateCode];
                return (
                  <Row
                    key={districtKey}
                    data={districts[districtKey]}
                    districtName={districts[districtKey].districtName}
                    {...{
                      regionHighlighted,
                      setRegionHighlighted,
                      expandTable,
                      tableStatistics,
                      getTableStatistic,
                      noDistrictData,
                    }}
                  />
                );
              })}

          <Row
            key={'TT'}
            data={states['TT']}
            stateCode={'TT'}
            {...{
              regionHighlighted,
              setRegionHighlighted,
              expandTable,
              tableStatistics,
              getTableStatistic,
            }}
          />
        </div>
      </div>
      {showDistricts && (
        <div className="paginate">
          <div
            className={classnames('left', {disabled: page === 0})}
            onClick={handlePageClick.bind(this, -2)}
          >
            <ChevronsLeft size={16} />
          </div>
          <div
            className={classnames('left', {disabled: page === 0})}
            onClick={handlePageClick.bind(this, -1)}
          >
            <ChevronLeft size={16} />
          </div>
          <h5>{`${page + 1} / ${numPages}`}</h5>
          <div
            className={classnames('right', {disabled: page === numPages - 1})}
            onClick={handlePageClick.bind(this, 1)}
          >
            <ChevronRight size={16} />
          </div>
          <div
            className={classnames('right', {disabled: page === numPages - 1})}
            onClick={handlePageClick.bind(this, 2)}
          >
            <ChevronsRight size={16} />
          </div>
        </div>
      )}
    </div>
  );
}