Skip to content

Commit 927b1bc

Browse files
authored
docs: add two-columns layout example (#908)
* docs(examples): scaffold two columns layout example * fix consistent naming * chore: fix linting * chore: fix styles * chore: fix deps * chore: add missing dep for Codesandbox * apply suggestions from code review * remove useless media queries * apply suggestions from code review * docs(two-column-layout-example): add products plugin (#909) * docs(two-columns-layout-example): add products plugin * apply suggestions from review * remove useless type * use classic header for recent searches and fix padding * apply suggestions from code review * docs(two-column-layout-example): add categories, brands and faq plugins (#910) * chore(two-columns-layout-example): add categories plugin * chore(two-columns-layout-example): add brands plugin * chore(two-columns-layout-example): add faq plugin * apply suggestions from code review * remove debug option * docs(two-column-layout-example): add articles and popular plugins (#911) * chore(two-columns-layout-example): add articles plugin * chore(two-columns-layout-example): add popular plugin * apply suggestions from code review * docs(two-column-layout-example): add reshape functions and quick access plugin (#912) * chore(two-columns-layout-example): add reshape functions * docs(two-columns-layout-example): add quick access plugin * chore: move reshape functions at file top-level * apply suggestions from code review * docs(two-column-layout-example): add smart preview feature (#913) * docs(two-columns-layout-example): add smart preview feature * docs(two-columns-layout-example): add faq article preview * apply suggestions from code review * docs(two-column-layout-example): add no results state and popular categories plugin (#914) * docs(two-columns-layout-example): add no results state and popular categories plugin * chore: fix empty div with Fragment * chore: fix empty div when no recent searches exist * chore: fix casing * apply suggestions from code review * apply suggestions from pair session * fix empty recent searches * fix linting * apply suggestion from code review * smart preview no longer depends on the DOM * apply suggestions from feedback * apply suggestions from feedback * apply suggestion from code review * apply suggestion from feedback * remove smart preview for now * add quick access selected transition * fix see all link margin * add getItemInputValue for faq * apply feedback * apply feedback * apply feedback * apply last fixes
1 parent 6f59dd5 commit 927b1bc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2460
-1
lines changed

examples/two-column-layout/.gitignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# See https://help.github.com/ignore-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
6+
# testing
7+
/coverage
8+
9+
# production
10+
/dist
11+
/.cache
12+
/.parcel-cache
13+
14+
# misc
15+
.DS_Store
16+
.env.local
17+
.env.development.local
18+
.env.test.local
19+
.env.production.local
20+
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*

examples/two-column-layout/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Autocomplete with a two-column layout example
2+
3+
This example shows how to integrate Autocomplete with a two-column layout.
4+
5+
<p align="center"><img src="capture.png?raw=true" alt="A capture of the Autocomplete with a two-column layout example" /></p>
6+
7+
## Demo
8+
9+
[Access the demo](https://codesandbox.io/s/github/algolia/autocomplete/tree/next/examples/two-column-layout)
10+
11+
## How to run this example locally
12+
13+
### 1. Clone this repository
14+
15+
```sh
16+
git clone [email protected]:algolia/autocomplete.git
17+
```
18+
19+
### 2. Install the dependencies and run the server
20+
21+
```sh
22+
yarn
23+
yarn workspace @algolia/autocomplete-example-two-column-layout start
24+
```
25+
26+
Alternatively, you may use npm:
27+
28+
```sh
29+
cd examples/two-column-layout
30+
npm install
31+
npm run start
32+
```
33+
34+
Open <http://localhost:1234> to see your app.
429 KB
Loading
43.1 KB
Loading

examples/two-column-layout/index.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<html lang="en">
2+
<head>
3+
<meta charset="UTF-8" />
4+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
5+
6+
<link rel="shortcut icon" href="favicon.png" type="image/x-icon" />
7+
<link rel="stylesheet" href="style.css" />
8+
9+
<title>Autocomplete with a two-column layout</title>
10+
</head>
11+
12+
<body>
13+
<noscript> You need to enable JavaScript to run this app. </noscript>
14+
15+
<div class="container">
16+
<div id="autocomplete"></div>
17+
</div>
18+
19+
<script src="src/app.tsx"></script>
20+
</body>
21+
</html>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "@algolia/autocomplete-example-two-column-layout",
3+
"description": "Autocomplete example with two-column layout",
4+
"version": "1.5.3",
5+
"private": true,
6+
"license": "MIT",
7+
"scripts": {
8+
"build": "parcel build index.html",
9+
"start": "parcel index.html"
10+
},
11+
"dependencies": {
12+
"@algolia/autocomplete-core": "1.5.3",
13+
"@algolia/autocomplete-js": "1.5.3",
14+
"@algolia/autocomplete-plugin-query-suggestions": "1.5.3",
15+
"@algolia/autocomplete-plugin-recent-searches": "1.5.3",
16+
"@algolia/autocomplete-theme-classic": "1.5.3",
17+
"@algolia/autocomplete-shared": "1.5.3",
18+
"@algolia/client-search": "4.12.1",
19+
"algoliasearch": "4.12.1",
20+
"blurhash": "^1.1.5",
21+
"dequal": "^2.0.2",
22+
"preact": "10.5.14",
23+
"ramda": "^0.28.0",
24+
"typescript": "^4.6.2"
25+
},
26+
"devDependencies": {
27+
"parcel": "2.0.0-beta.2"
28+
},
29+
"keywords": [
30+
"algolia",
31+
"autocomplete",
32+
"typescript"
33+
]
34+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/** @jsx h */
2+
import { autocomplete } from '@algolia/autocomplete-js';
3+
import { h, render } from 'preact';
4+
import { pipe } from 'ramda';
5+
6+
import { createFillWith, uniqBy } from './functions';
7+
import { articlesPlugin } from './plugins/articlesPlugin';
8+
import { brandsPlugin } from './plugins/brandsPlugin';
9+
import { categoriesPlugin } from './plugins/categoriesPlugin';
10+
import { faqPlugin } from './plugins/faqPlugin';
11+
import { popularCategoriesPlugin } from './plugins/popularCategoriesPlugin';
12+
import { popularPlugin } from './plugins/popularPlugin';
13+
import { productsPlugin } from './plugins/productsPlugin';
14+
import { querySuggestionsPlugin } from './plugins/querySuggestionsPlugin';
15+
import { quickAccessPlugin } from './plugins/quickAccessPlugin';
16+
import { recentSearchesPlugin } from './plugins/recentSearchesPlugin';
17+
import { cx, hasSourceActiveItem, isDetached } from './utils';
18+
19+
import '@algolia/autocomplete-theme-classic';
20+
21+
const removeDuplicates = uniqBy(({ source, item }) => {
22+
const sourceIds = ['recentSearchesPlugin', 'querySuggestionsPlugin'];
23+
if (sourceIds.indexOf(source.sourceId) === -1) {
24+
return item;
25+
}
26+
27+
return source.sourceId === 'querySuggestionsPlugin' ? item.query : item.label;
28+
});
29+
30+
const fillWith = createFillWith({
31+
mainSourceId: 'querySuggestionsPlugin',
32+
limit: isDetached() ? 6 : 10,
33+
});
34+
35+
const combine = pipe(removeDuplicates, fillWith);
36+
37+
autocomplete({
38+
container: '#autocomplete',
39+
placeholder: 'Search products, articles, and FAQs',
40+
autoFocus: true,
41+
openOnFocus: true,
42+
plugins: [
43+
recentSearchesPlugin,
44+
querySuggestionsPlugin,
45+
categoriesPlugin,
46+
brandsPlugin,
47+
faqPlugin,
48+
productsPlugin,
49+
articlesPlugin,
50+
popularPlugin,
51+
quickAccessPlugin,
52+
popularCategoriesPlugin,
53+
],
54+
reshape({ sourcesBySourceId, sources, state }) {
55+
const {
56+
recentSearchesPlugin: recentSearches,
57+
querySuggestionsPlugin: querySuggestions,
58+
categoriesPlugin: categories,
59+
brandsPlugin: brands,
60+
faqPlugin: faq,
61+
popularPlugin: popular,
62+
popularCategoriesPlugin: popularCategories,
63+
...rest
64+
} = sourcesBySourceId;
65+
66+
const sourceIdsToExclude = ['popularPlugin', 'popularCategoriesPlugin'];
67+
const shouldDisplayPopularCategories = sources.every((source) => {
68+
if (sourceIdsToExclude.indexOf(source.sourceId) !== -1) {
69+
return true;
70+
}
71+
return source.getItems().length === 0;
72+
});
73+
74+
return [
75+
combine(recentSearches, querySuggestions, categories, brands, faq),
76+
[
77+
!state.query && popular,
78+
...Object.values(rest),
79+
shouldDisplayPopularCategories && popularCategories,
80+
].filter(Boolean),
81+
];
82+
},
83+
render({ elements, state, Fragment }, root) {
84+
const {
85+
recentSearchesPlugin: recentSearches,
86+
querySuggestionsPlugin: querySuggestions,
87+
categoriesPlugin: categories,
88+
brandsPlugin: brands,
89+
faqPlugin: faq,
90+
productsPlugin: products,
91+
articlesPlugin: articles,
92+
popularPlugin: popular,
93+
quickAccessPlugin: quickAccess,
94+
popularCategoriesPlugin: popularCategories,
95+
} = elements;
96+
97+
const sourceIdsToExclude = ['popularPlugin', 'popularCategoriesPlugin'];
98+
const hasResults =
99+
state.collections
100+
.filter(
101+
({ source }) => sourceIdsToExclude.indexOf(source.sourceId) === -1
102+
)
103+
.reduce((prev, curr) => prev + curr.items.length, 0) > 0;
104+
105+
render(
106+
<div className="aa-PanelLayout aa-Panel--scrollable">
107+
{!hasResults && (
108+
<div className="aa-NoResultsQuery">
109+
No results for "{state.query}".
110+
</div>
111+
)}
112+
113+
<div className="aa-PanelSections">
114+
<div className="aa-PanelSection--left">
115+
{hasResults ? (
116+
(!state.query && recentSearches && (
117+
<Fragment>
118+
<div className="aa-SourceHeader">
119+
<span className="aa-SourceHeaderTitle">
120+
Recent searches
121+
</span>
122+
<div className="aa-SourceHeaderLine" />
123+
</div>
124+
{recentSearches}
125+
</Fragment>
126+
)) ||
127+
(state.query && (
128+
<Fragment>
129+
<div className="aa-SourceHeader">
130+
<span className="aa-SourceHeaderTitle">Suggestions</span>
131+
<div className="aa-SourceHeaderLine" />
132+
</div>
133+
134+
<div className="aa-PanelSectionSources">
135+
{recentSearches}
136+
{querySuggestions}
137+
{categories}
138+
{brands}
139+
{faq}
140+
</div>
141+
</Fragment>
142+
))
143+
) : (
144+
<div className="aa-NoResultsAdvices">
145+
<ul className="aa-NoResultsAdvicesList">
146+
<li>Double-check your spelling</li>
147+
<li>Use fewer keywords</li>
148+
<li>Search for a less specific item</li>
149+
<li>Try navigate using on the of the popular categories</li>
150+
</ul>
151+
</div>
152+
)}
153+
154+
{!state.query && (
155+
<div className="aa-PanelSection--popular">{popular}</div>
156+
)}
157+
</div>
158+
<div className="aa-PanelSection--right">
159+
{products && (
160+
<div className="aa-PanelSection--products">
161+
<div className="aa-PanelSectionSource">{products}</div>
162+
</div>
163+
)}
164+
{articles && (
165+
<div className="aa-PanelSection--articles">
166+
<div className="aa-PanelSectionSource">{articles}</div>
167+
</div>
168+
)}
169+
170+
{quickAccess && (
171+
<div
172+
className={cx(
173+
'aa-PanelSection--quickAccess aa-PanelSection--zoomable',
174+
hasSourceActiveItem('quickAccessPlugin', state) &&
175+
'aa-PanelSection--active'
176+
)}
177+
>
178+
{quickAccess}
179+
</div>
180+
)}
181+
182+
{!hasResults && (
183+
<div
184+
className={cx(
185+
'aa-PanelSection--popularCategories aa-PanelSection--zoomable',
186+
hasSourceActiveItem('popularCategoriesPlugin', state) &&
187+
'aa-PanelSection--active'
188+
)}
189+
>
190+
{popularCategories}
191+
</div>
192+
)}
193+
</div>
194+
</div>
195+
</div>,
196+
root
197+
);
198+
},
199+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/** @jsx h */
2+
import { decode } from 'blurhash';
3+
import { h } from 'preact';
4+
import { useEffect, useRef } from 'preact/hooks';
5+
6+
export interface BlurhashProps {
7+
hash: string;
8+
width: number;
9+
height: number;
10+
punch?: number;
11+
}
12+
13+
export function Blurhash({ hash, width, height, punch = 1 }: BlurhashProps) {
14+
const canvasRef = useRef<HTMLCanvasElement>(null);
15+
16+
useEffect(() => {
17+
const pixels = decode(hash, width, height, punch);
18+
19+
const ctx = canvasRef.current.getContext('2d');
20+
const imageData = ctx.createImageData(width, height);
21+
imageData.data.set(pixels);
22+
ctx.putImageData(imageData, 0, 0);
23+
}, [hash, width, height, punch]);
24+
25+
return (
26+
<canvas
27+
ref={canvasRef}
28+
height={height}
29+
width={width}
30+
className="aa-BlurhashCanvas"
31+
/>
32+
);
33+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/** @jsx h */
2+
import { h } from 'preact';
3+
4+
import { intersperse } from '../utils';
5+
6+
import { ChevronRightIcon } from './Icons';
7+
8+
type BreadcrumbProps = {
9+
items: JSX.Element[];
10+
};
11+
12+
export function Breadcrumb({ items }: BreadcrumbProps) {
13+
return (
14+
<div className="aa-Breadcrumb">
15+
{intersperse(
16+
items,
17+
<div className="aa-ItemIcon aa-ItemIcon--noBorder aa-FavoriteIcon">
18+
<ChevronRightIcon />
19+
</div>
20+
)}
21+
</div>
22+
);
23+
}

0 commit comments

Comments
 (0)