Skip to content

Commit 4aa84bc

Browse files
committed
[client] adding links to explore page in item pages
Adding an anchor system for the "result" part of the page. Part of #65
1 parent 643c1ac commit 4aa84bc

7 files changed

Lines changed: 68 additions & 25 deletions

File tree

code/client/src/components/Collapsable.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { isNil } from 'lodash';
33
import { ReactNode, useState, type FC, type HTMLAttributes, type PropsWithChildren } from 'react';
44
import { RiArrowDownSLine, RiArrowRightSLine } from 'react-icons/ri';
55

6-
type CollapsableProps = HTMLAttributes<HTMLElement> & {
6+
type CollapsableProps = Omit<HTMLAttributes<HTMLElement>, 'title'> & {
77
title: ReactNode;
88
defaultOpen?: boolean;
99
};
@@ -17,14 +17,14 @@ export const Collapsable: FC<PropsWithChildren<CollapsableProps>> = ({
1717
return (
1818
<section {...htmlAttributs}>
1919
<button
20-
className="btn ps-0 with-icon fw-medium mb-1"
20+
className="btn ps-0 with-icon fw-medium mb-1 d-flex align-items-center"
2121
type="button"
2222
onClick={(e) => {
2323
e.stopPropagation();
2424
setShow(!show);
2525
}}
2626
>
27-
{show ? <RiArrowDownSLine /> : <RiArrowRightSLine />} {title}
27+
{show ? <RiArrowDownSLine size={'2em'} /> : <RiArrowRightSLine size={'2em'} />} {title}
2828
</button>
2929

3030
<div className={cx('collapse', show && 'show')}>{children}</div>

code/client/src/components/ItemsList.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -198,16 +198,14 @@ export const ItemsList: FC<{ itemType: ItemType }> = ({ itemType }) => {
198198
);
199199

200200
return (
201-
<section>
202-
<ListComponent
203-
element={ItemComponent}
204-
elementProps={{ itemType }}
205-
getDataId={(data) => data.id}
206-
loadData={loadData}
207-
bottom={Bottom}
208-
list={List}
209-
top={Top}
210-
/>
211-
</section>
201+
<ListComponent
202+
element={ItemComponent}
203+
elementProps={{ itemType }}
204+
getDataId={(data) => data.id}
205+
loadData={loadData}
206+
bottom={Bottom}
207+
list={List}
208+
top={Top}
209+
/>
212210
);
213211
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useEffect, useRef } from 'react';
2+
import { useLocation } from 'react-router-dom';
3+
4+
function ScrollToAnchor() {
5+
const location = useLocation();
6+
const lastHash = useRef('');
7+
8+
// listen to location change using useEffect with location as dependency
9+
// https://jasonwatmore.com/react-router-v6-listen-to-location-route-change-without-history-listen
10+
useEffect(() => {
11+
if (location.hash) {
12+
lastHash.current = location.hash.slice(1); // safe hash for further use after navigation
13+
}
14+
15+
if (lastHash.current && document.getElementById(lastHash.current)) {
16+
setTimeout(() => {
17+
document
18+
.getElementById(lastHash.current)
19+
?.scrollIntoView({ behavior: 'smooth', block: 'start' });
20+
lastHash.current = '';
21+
}, 100);
22+
}
23+
}, [location]);
24+
25+
return null;
26+
}
27+
28+
export default ScrollToAnchor;

code/client/src/styles/_explore.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ main {
2222
height: 150px;
2323
}
2424

25+
#result {
26+
min-height: 100vh;
27+
}
28+
2529
@media (max-width: 1370px) {
2630
.explore-header {
2731
.explore-date {

code/client/src/views/Explore.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { filtersStateToSearchFilters } from '../utils/filters';
2727

2828
export const Explore: FC = () => {
2929
const { type: inputType } = useParams();
30-
const { search } = useLocation();
30+
const { search, hash } = useLocation();
3131
const navigate = useNavigate();
3232
const client = useApolloClient();
3333

@@ -89,8 +89,10 @@ export const Explore: FC = () => {
8989
useEffect(() => {
9090
const newSearch = stateToSearch(state).toString();
9191
if (newSearch !== search)
92-
navigate(`/explore/${selectedType}${newSearch ? '?' + newSearch : ''}`, { replace: true });
93-
}, [navigate, search, selectedType, state]);
92+
navigate(`/explore/${selectedType}${newSearch ? '?' + newSearch : ''}${hash}`, {
93+
replace: true,
94+
});
95+
}, [navigate, search, selectedType, state, hash]);
9496

9597
return (
9698
<FacetsRoot
@@ -153,7 +155,9 @@ export const Explore: FC = () => {
153155
</section>
154156

155157
{/* CORE ITEMS LIST */}
156-
<ItemsList key={fingerprint} itemType={selectedType} />
158+
<section id="result">
159+
<ItemsList key={fingerprint} itemType={selectedType} />
160+
</section>
157161
</main>
158162
</FacetsRoot>
159163
);

code/client/src/views/ItemView.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { LoaderFill } from '@ouestware/loaders';
22
import cx from 'classnames';
33
import { ReactNode, useMemo, type FC } from 'react';
4-
import { RiAddCircleLine, RiFile3Line, RiPriceTag3Line } from 'react-icons/ri';
5-
import { useParams } from 'react-router-dom';
4+
import { RiFile3Line, RiPriceTag3Line } from 'react-icons/ri';
5+
import { Link, useParams } from 'react-router-dom';
66

77
import { Collapsable } from '../components/Collapsable';
88
import { InCartButton } from '../components/edition/InCartButton.tsx';
@@ -31,7 +31,7 @@ import { useItemCounts } from '../hooks/useItemCounts.ts';
3131
import { getMessageName } from '../utils/data.ts';
3232

3333
// eslint-disable-next-line @typescript-eslint/no-explicit-any
34-
type RelatedDefinition<T = any> = ListWithLoadMoreProps<T> & { title: ReactNode };
34+
type RelatedDefinition<T = any> = ListWithLoadMoreProps<T> & { title: ReactNode; type: ItemType };
3535

3636
export const ItemView: FC = () => {
3737
const { enabled } = useEditionContext();
@@ -63,6 +63,7 @@ export const ItemView: FC = () => {
6363

6464
return [
6565
{
66+
type,
6667
title: (
6768
<span className="fs-2">
6869
<ItemIcon type={type} /> {ITEM_TYPE_LABELS_PLURAL[type]} {total && `(${total})`}
@@ -124,13 +125,19 @@ export const ItemView: FC = () => {
124125

125126
{relatedItems.map((related, index) => (
126127
<Collapsable key={index} title={related.title} className="mb-2" defaultOpen>
127-
{editionEnabled && (
128-
<div className="mb-3">
128+
<div className="mb-3 d-flex gap-1">
129+
<Link
130+
to={`/explore/${related.type}?${inputType}|values=${id}#result`}
131+
className="btn btn-dark"
132+
>
133+
View in explore page
134+
</Link>
135+
{/* {editionEnabled && (
129136
<button className="btn btn-purple-300 with-icon">
130137
<RiAddCircleLine /> Add item
131138
</button>
132-
</div>
133-
)}
139+
)} */}
140+
</div>
134141
<ListWithLoadMore className="row" {...related} />
135142
</Collapsable>
136143
))}

code/client/src/views/Root.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { FaCheckCircle, FaExclamationTriangle, FaInfoCircle } from 'react-icons/
99
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
1010

1111
import { Error } from '../components/error';
12+
import ScrollToAnchor from '../components/ScrollToAnchor.tsx';
1213
import { DEFAULT_EDITION_DATA, EditionContext, EditionItem } from '../core/edition.ts';
1314
import { ApolloProvider } from '../core/graphql';
1415
import { deserializeEditionState, EDITION_STATE_KEY } from '../utils/edition.ts';
@@ -80,6 +81,7 @@ export const Root: FC = () => {
8081
<Route path="/" element={<Navigate to="/explore/company" replace />} />
8182
</Routes>
8283
</ModalProvider>
84+
<ScrollToAnchor />
8385
</BrowserRouter>
8486
</EditionContext.Provider>
8587
</NotificationProvider>

0 commit comments

Comments
 (0)