Skip to content

Commit

Permalink
Add subcategory zoom view (#133)
Browse files Browse the repository at this point in the history
Closes #111

Signed-off-by: Cintia Sanchez Garcia <[email protected]>
  • Loading branch information
cynthia-sg authored Aug 28, 2023
1 parent 556eb56 commit 64d4af8
Show file tree
Hide file tree
Showing 24 changed files with 421 additions and 116 deletions.
4 changes: 0 additions & 4 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import Layout from './layout';
import Landscape from './layout/explore';
import Guide from './layout/guide';
import NotFound from './layout/notFound';
import Stats from './layout/stats';
import Acquisitions from './layout/stats';
import itemsDataGetter from './utils/itemsDataGetter';

const App = () => {
Expand All @@ -29,8 +27,6 @@ const App = () => {
<Route path="/" element={<Layout items={data.items} />}>
<Route index element={<Landscape data={data} />} />
<Route path="/guide" element={<Guide />} />
<Route path="/stats" element={<Stats />} />
<Route path="/acquisitions" element={<Acquisitions />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
Expand Down
5 changes: 0 additions & 5 deletions web/src/layout/acquisitions/index.tsx

This file was deleted.

8 changes: 8 additions & 0 deletions web/src/layout/common/FullScreenModal.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.modal {
background-color: rgba(0, 0, 0, 0.75);
z-index: 1050;
}

.closeWrapper {
right: 1rem;
}
70 changes: 70 additions & 0 deletions web/src/layout/common/FullScreenModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import isUndefined from 'lodash/isUndefined';
import { MouseEvent as ReactMouseEvent, RefObject, useEffect, useState } from 'react';

import { useBodyScroll } from '../../hooks/useBodyScroll';
import { useOutsideClick } from '../../hooks/useOutsideClick';
import styles from './FullScreenModal.module.css';

interface Props {
children: JSX.Element | JSX.Element[];
open?: boolean;
onClose?: () => void;
refs?: RefObject<HTMLElement>[];
}

const FullScreenModal = (props: Props) => {
const [openStatus, setOpenStatus] = useState(props.open || false);
const outsideElements: RefObject<HTMLElement>[] = !isUndefined(props.refs) ? props.refs : [];

useBodyScroll(openStatus, 'modal');
useOutsideClick(outsideElements, openStatus, () => {
closeModal();
});

const closeModal = () => {
setOpenStatus(false);
if (!isUndefined(props.onClose)) {
props.onClose();
}
};

useEffect(() => {
if (!isUndefined(props.open)) {
setOpenStatus(props.open);
}
}, [props.open]);

useEffect(() => {
const handleEsc = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
closeModal();
}
};

window.addEventListener('keydown', handleEsc);
return () => {
window.removeEventListener('keydown', handleEsc);
};
}, []); /* eslint-disable-line react-hooks/exhaustive-deps */

if (!openStatus) return null;

return (
<div className={`position-fixed overflow-hidden p-3 top-0 bottom-0 start-0 end-0 ${styles.modal}`} role="dialog">
<div className={`position-absolute ${styles.closeWrapper}`}>
<button
type="button"
className={`btn-close btn-close-white opacity-100 fs-5 ${styles.close}`}
onClick={(e: ReactMouseEvent<HTMLButtonElement>) => {
e.preventDefault();
closeModal();
}}
aria-label="Close"
></button>
</div>
<div className="d-flex flex-column h-100 w-100">{props.children}</div>
</div>
);
};

export default FullScreenModal;
16 changes: 16 additions & 0 deletions web/src/layout/common/SVGIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,22 @@ const SVGIcon = (props: Props) => (
</svg>
);

case SVGIconKind.MagnifyingGlass:
return (
<svg
className={props.className}
stroke="currentColor"
fill="currentColor"
strokeWidth="0"
viewBox="0 0 256 256"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M156,112a12,12,0,0,1-12,12H124v20a12,12,0,0,1-24,0V124H80a12,12,0,0,1,0-24h20V80a12,12,0,0,1,24,0v20h20A12,12,0,0,1,156,112Zm76.49,120.49a12,12,0,0,1-17,0L168,185a92.12,92.12,0,1,1,17-17l47.54,47.53A12,12,0,0,1,232.49,232.49ZM112,180a68,68,0,1,0-68-68A68.08,68.08,0,0,0,112,180Z"></path>
</svg>
);

case SVGIconKind.MailingList:
return (
<svg
Expand Down
7 changes: 4 additions & 3 deletions web/src/layout/common/Searchbar.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import classNames from 'classnames';
import { isNull, isUndefined } from 'lodash';
import { ChangeEvent, KeyboardEvent, useEffect, useRef, useState } from 'react';
import { ChangeEvent, KeyboardEvent, useContext, useEffect, useRef, useState } from 'react';

import { useOutsideClick } from '../../hooks/useOutsideClick';
import { BaseItem, SVGIconKind } from '../../types';
import { AppContext, Context } from '../context/AppContext';
import HoverableItem from './HoverableItem';
import MaturityBadge from './MaturityBadge';
import styles from './Searchbar.module.css';
import SVGIcon from './SVGIcon';

interface Props {
items: BaseItem[];
openItem: (itemId: string) => void;
}

const SEARCH_DELAY = 3 * 100; // 300ms
const MIN_CHARACTERS_SEARCH = 2;

const Searchbar = (props: Props) => {
const { updateActiveItemId } = useContext(AppContext) as Context;
const inputEl = useRef<HTMLInputElement>(null);
const dropdownRef = useRef(null);
const [value, setValue] = useState<string>('');
Expand Down Expand Up @@ -66,7 +67,7 @@ const Searchbar = (props: Props) => {
forceBlur();
setValue('');
cleanItemsSearch();
props.openItem(selectedItemId);
updateActiveItemId(selectedItemId);
};

const forceBlur = (): void => {
Expand Down
26 changes: 9 additions & 17 deletions web/src/layout/common/itemModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import classNames from 'classnames';
import { isUndefined } from 'lodash';
import moment from 'moment';
import { useEffect, useState } from 'react';
import { useContext, useEffect, useState } from 'react';

import { Item, Repository, SVGIconKind } from '../../../types';
import cleanEmojis from '../../../utils/cleanEmojis';
import formatProfitLabel from '../../../utils/formatLabelProfit';
import itemsDataGetter from '../../../utils/itemsDataGetter';
import prettifyNumber from '../../../utils/prettifyNumber';
import { AppContext, Context } from '../../context/AppContext';
import ExternalLink from '../ExternalLink';
import Image from '../Image';
import { Loading } from '../Loading';
Expand All @@ -17,23 +18,14 @@ import SVGIcon from '../SVGIcon';
import styles from './ItemModal.module.css';
import ParticipationStats from './ParticipationStats';

interface Props {
activeItemId?: string;
removeActiveItem: () => void;
}

const ItemModal = (props: Props) => {
const [fullDataReady, setFullDataReady] = useState<boolean>(false);
const ItemModal = () => {
const { activeItemId, updateActiveItemId, fullDataReady } = useContext(AppContext) as Context;
const [itemInfo, setItemInfo] = useState<Item | null | undefined>(undefined);
let description = 'This item does not have a description available yet';
let stars: number | undefined;
let mainRepo: Repository | undefined;
let websiteUrl: string | undefined = itemInfo ? itemInfo.homepage_url : undefined;

itemsDataGetter.isReady({
updateStatus: (status: boolean) => setFullDataReady(status),
});

if (
itemInfo &&
itemInfo.crunchbase_data &&
Expand Down Expand Up @@ -83,23 +75,23 @@ const ItemModal = (props: Props) => {
async function fetchItemInfo() {
try {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
setItemInfo(await itemsDataGetter.get(props.activeItemId!));
setItemInfo(await itemsDataGetter.get(activeItemId!));
} catch {
setItemInfo(null);
}
}

if (props.activeItemId && fullDataReady) {
if (activeItemId && fullDataReady) {
fetchItemInfo();
} else {
setItemInfo(undefined);
}
}, [props.activeItemId, fullDataReady]);
}, [activeItemId, fullDataReady]);

if (isUndefined(props.activeItemId)) return null;
if (isUndefined(activeItemId)) return null;

return (
<Modal size="xl" open modalDialogClassName={styles.modalDialog} onClose={() => props.removeActiveItem()}>
<Modal size="xl" open modalDialogClassName={styles.modalDialog} onClose={() => updateActiveItemId()}>
{itemInfo ? (
<div className="d-flex flex-column p-3">
<div className="d-flex flex-row align-items-center">
Expand Down
37 changes: 37 additions & 0 deletions web/src/layout/common/zoomModal/ZoomModal.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.loadingWrapper {
min-height: 500px;
}

.wrapper {
background-color: var(--bs-white);
/* Tooltip height + Subcategory title height + Featured card height */
min-height: calc(262px + 35px + 68px * 2);
}

.catTitle {
writing-mode: vertical-rl;
text-orientation: mixed;
transform: rotate(180deg);
width: 2.5rem;
font-size: 0.8rem;
}

.subcatTitle {
font-size: 0.8rem;
height: 2rem;
}

.content {
background-color: var(--bs-gray-100);
padding: 0.6rem;
}

.grid {
--card-size-width: 75px;
--card-size-height: 68px;

display: grid;
grid-template-columns: repeat(auto-fit, var(--card-size-width));
grid-auto-rows: var(--card-size-height);
gap: var(--card-gap);
}
Loading

0 comments on commit 64d4af8

Please sign in to comment.