algolia-experiences 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "algolia-experiences",
3
3
  "license": "MIT",
4
- "version": "1.0.1",
4
+ "version": "1.0.2",
5
5
  "main": "src/index.ts",
6
6
  "jsdelivr": "dist/algolia-experiences.production.min.js",
7
7
  "unpkg": "dist/algolia-experiences.production.min.js",
8
+ "files": [
9
+ "dist"
10
+ ],
8
11
  "dependencies": {
9
12
  "instantsearch.js": "^4.73.2",
10
13
  "algoliasearch": "^4.23.2"
package/rollup.config.js DELETED
@@ -1,76 +0,0 @@
1
- import babel from 'rollup-plugin-babel';
2
- import commonjs from 'rollup-plugin-commonjs';
3
- import filesize from 'rollup-plugin-filesize';
4
- import resolve from 'rollup-plugin-node-resolve';
5
- import replace from 'rollup-plugin-replace';
6
- import { uglify } from 'rollup-plugin-uglify';
7
-
8
- import packageJson from '../../package.json';
9
-
10
- const version =
11
- process.env.NODE_ENV === 'production'
12
- ? packageJson.version
13
- : `UNRELEASED (${new Date().toUTCString()})`;
14
- const algolia = '© Algolia, Inc. and contributors; MIT License';
15
- const link = 'https://github.com/algolia/instantsearch';
16
- const license = `/*! algolia-experiences ${version} | ${algolia} | ${link} */`;
17
-
18
- const plugins = [
19
- resolve({
20
- browser: true,
21
- preferBuiltins: false,
22
- extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'],
23
- }),
24
- babel({
25
- rootMode: 'upward',
26
- runtimeHelpers: true,
27
- exclude: /node_modules|algoliasearch-helper/,
28
- extensions: ['.js', '.ts', '.tsx'],
29
- }),
30
- commonjs(),
31
- filesize({
32
- showMinifiedSize: false,
33
- showGzippedSize: true,
34
- }),
35
- ];
36
-
37
- const createConfiguration = ({ mode, filename }) => ({
38
- input: 'src/index.ts',
39
- output: {
40
- file: `dist/${filename}`,
41
- name: 'instantsearch',
42
- format: 'umd',
43
- banner: license,
44
- sourcemap: true,
45
- },
46
- onwarn(warning, warn) {
47
- if (warning.code === 'CIRCULAR_DEPENDENCY')
48
- throw new Error(warning.message);
49
-
50
- warn(warning);
51
- },
52
- plugins: [
53
- ...plugins,
54
- replace({
55
- __DEV__: mode === 'development',
56
- 'process.env.NODE_ENV': JSON.stringify('production'),
57
- }),
58
- mode === 'production' &&
59
- uglify({
60
- output: {
61
- preamble: license,
62
- },
63
- }),
64
- ].filter(Boolean),
65
- });
66
-
67
- export default [
68
- createConfiguration({
69
- mode: 'development',
70
- filename: 'algolia-experiences.development.js',
71
- }),
72
- createConfiguration({
73
- mode: 'production',
74
- filename: 'algolia-experiences.production.min.js',
75
- }),
76
- ];
@@ -1,283 +0,0 @@
1
- import type { Configuration } from './types';
2
-
3
- const CONFIGURATION_OBJECT: Record<string, Configuration> = {
4
- qsdfqsdf: {
5
- id: 'qsdfqsdf',
6
- indexName: 'instant_search',
7
- children: [
8
- {
9
- type: 'ais.searchBox',
10
- parameters: {},
11
- },
12
- {
13
- type: 'columns',
14
- children: [
15
- [
16
- {
17
- type: 'ais.refinementList',
18
- parameters: {
19
- attribute: 'brand',
20
- header: 'Brand',
21
- },
22
- },
23
- ],
24
- [
25
- {
26
- type: 'ais.hits',
27
- parameters: {},
28
- children: [
29
- {
30
- type: 'span',
31
- parameters: {
32
- text: [
33
- { type: 'string', value: 'cols: ' },
34
- { type: 'highlight', path: ['name'] },
35
- ],
36
- },
37
- },
38
- ],
39
- },
40
- ],
41
- ],
42
- },
43
- {
44
- type: 'ais.configure',
45
- parameters: {
46
- hitsPerPage: 2,
47
- },
48
- },
49
- {
50
- type: 'ais.hits',
51
- parameters: {},
52
- children: [
53
- {
54
- type: 'span',
55
- parameters: {
56
- text: [
57
- { type: 'string', value: 'one: ' },
58
- { type: 'highlight', path: ['name'] },
59
- ],
60
- },
61
- },
62
- ],
63
- },
64
- ],
65
- },
66
- 'other-one': {
67
- id: 'other-one',
68
- indexName: 'instant_search',
69
- children: [
70
- {
71
- type: 'ais.configure',
72
- parameters: {
73
- hitsPerPage: 3,
74
- },
75
- },
76
- {
77
- type: 'ais.hits',
78
- parameters: {},
79
- children: [
80
- {
81
- type: 'span',
82
- parameters: {
83
- text: [
84
- { type: 'string', value: 'other: ' },
85
- { type: 'attribute', path: ['name'] },
86
- ],
87
- },
88
- },
89
- ],
90
- },
91
- {
92
- type: 'ais.pagination',
93
- parameters: {
94
- padding: 2,
95
- },
96
- },
97
- {
98
- type: 'ais.trendingItems',
99
- parameters: { limit: 4 },
100
- children: [
101
- {
102
- type: 'span',
103
- parameters: {
104
- text: [
105
- { type: 'string', value: 'trending: ' },
106
- { type: 'attribute', path: ['name'] },
107
- ],
108
- },
109
- },
110
- {
111
- type: 'image',
112
- parameters: {
113
- src: [{ type: 'attribute', path: ['image'] }],
114
- alt: [{ type: 'string', value: '' }],
115
- },
116
- },
117
- ],
118
- },
119
- ],
120
- },
121
- 'category:audio': {
122
- id: 'category:audio',
123
- indexName: 'instant_search',
124
- children: [
125
- {
126
- type: 'ais.configure',
127
- parameters: {
128
- hitsPerPage: 9,
129
- filters: 'categories:"Audio"',
130
- },
131
- },
132
- {
133
- type: 'columns',
134
- children: [
135
- [
136
- {
137
- type: 'ais.refinementList',
138
- parameters: {
139
- attribute: 'brand',
140
- header: 'brand',
141
- collapsed: true,
142
- searchable: true,
143
- },
144
- },
145
- {
146
- type: 'ais.refinementList',
147
- parameters: {
148
- attribute: 'type',
149
- header: 'type',
150
- collapsed: true,
151
- searchable: true,
152
- },
153
- },
154
- {
155
- type: 'ais.rangeInput',
156
- parameters: {
157
- attribute: 'price',
158
- header: 'price',
159
- collapsed: true,
160
- },
161
- },
162
- {
163
- type: 'ais.toggleRefinement',
164
- parameters: {
165
- attribute: 'free_shipping',
166
- header: 'free shipping',
167
- collapsed: true,
168
- },
169
- },
170
- ],
171
- [
172
- {
173
- type: 'ais.hits',
174
- parameters: {},
175
- children: [
176
- {
177
- type: 'link',
178
- parameters: { href: [{ type: 'attribute', path: ['url'] }] },
179
- children: [
180
- {
181
- type: 'image',
182
- parameters: {
183
- src: [{ type: 'attribute', path: ['image'] }],
184
- alt: [{ type: 'string', value: '' }],
185
- },
186
- },
187
- {
188
- type: 'div',
189
- parameters: {
190
- class: [{ type: 'string', value: '__flex' }],
191
- },
192
- children: [
193
- {
194
- type: 'span',
195
- parameters: {
196
- text: [{ type: 'attribute', path: ['name'] }],
197
- },
198
- },
199
- {
200
- type: 'span',
201
- parameters: {
202
- class: [{ type: 'string', value: '__bold' }],
203
- text: [
204
- { type: 'string', value: '$' },
205
- { type: 'attribute', path: ['price'] },
206
- ],
207
- },
208
- },
209
- ],
210
- },
211
- ],
212
- },
213
- ],
214
- },
215
- {
216
- type: 'ais.pagination',
217
- parameters: {
218
- padding: 2,
219
- },
220
- },
221
- {
222
- type: 'ais.trendingItems',
223
- parameters: {
224
- limit: 4,
225
- facetName: 'categories',
226
- facetValue: 'Audio',
227
- },
228
- children: [
229
- {
230
- type: 'link',
231
- parameters: { href: [{ type: 'attribute', path: ['url'] }] },
232
- children: [
233
- {
234
- type: 'image',
235
- parameters: {
236
- src: [{ type: 'attribute', path: ['image'] }],
237
- alt: [{ type: 'string', value: '' }],
238
- },
239
- },
240
- {
241
- type: 'div',
242
- parameters: {
243
- class: [{ type: 'string', value: '__flex' }],
244
- },
245
- children: [
246
- {
247
- type: 'span',
248
- parameters: {
249
- text: [{ type: 'attribute', path: ['name'] }],
250
- },
251
- },
252
- {
253
- type: 'span',
254
- parameters: {
255
- class: [{ type: 'string', value: '__bold' }],
256
- text: [
257
- { type: 'string', value: '$' },
258
- { type: 'attribute', path: ['price'] },
259
- ],
260
- },
261
- },
262
- ],
263
- },
264
- ],
265
- },
266
- ],
267
- },
268
- ],
269
- ],
270
- },
271
- ],
272
- },
273
- };
274
- const CONFIGURATION_MAP = new Map<string, Configuration>(
275
- Object.entries(CONFIGURATION_OBJECT)
276
- );
277
- const FAKE_DELAY = 1000;
278
-
279
- export function fakeFetchConfiguration(ids: string[]) {
280
- return new Promise((resolve) => setTimeout(resolve, FAKE_DELAY)).then(() =>
281
- ids.map((id) => CONFIGURATION_MAP.get(id)!).filter(Boolean)
282
- );
283
- }
@@ -1,29 +0,0 @@
1
- export function getSettings(): { appId: string; apiKey: string } {
2
- const metaConfiguration = document.querySelector<HTMLMetaElement>(
3
- 'meta[name="instantsearch-configuration"]'
4
- );
5
-
6
- if (!metaConfiguration || !metaConfiguration.content) {
7
- throw new Error('No meta tag found');
8
- }
9
-
10
- const { appId, apiKey } = JSON.parse(metaConfiguration.content);
11
-
12
- if (!appId || !apiKey) {
13
- throw new Error('Missing appId or apiKey in the meta tag');
14
- }
15
-
16
- return { appId, apiKey };
17
- }
18
-
19
- export function getElements() {
20
- const elements = new Map<string, HTMLElement>();
21
- document
22
- .querySelectorAll<HTMLElement>('[data-instantsearch-id]')
23
- .forEach((element) => {
24
- const id = element.dataset.instantsearchId!;
25
- elements.set(id, element);
26
- });
27
-
28
- return elements;
29
- }
package/src/render.tsx DELETED
@@ -1,250 +0,0 @@
1
- /** @jsx h */
2
- import { getPropertyByPath } from 'instantsearch.js/es/lib/utils';
3
- import { index, panel } from 'instantsearch.js/es/widgets';
4
- import { h, Fragment } from 'preact';
5
-
6
- import { error } from './util';
7
- import { widgets } from './widgets';
8
-
9
- import type {
10
- Child,
11
- Configuration,
12
- PanelWidget,
13
- PanelWidgetTypes,
14
- TemplateAttribute,
15
- TemplateChild,
16
- TemplateText,
17
- TemplateWidgetTypes,
18
- } from './types';
19
- import type { Widget } from 'instantsearch.js';
20
- import type { ComponentChildren, JSX } from 'preact';
21
-
22
- export function injectStyles() {
23
- const style = document.createElement('style');
24
- // @TODO: decide if this should be for all columns or only a specific type
25
- style.textContent = `
26
- .ais-Columns {
27
- display: grid;
28
- grid-template-columns: minmax(min-content, 200px) 1fr;
29
- gap: 1em;
30
- }
31
- `;
32
- document.head.appendChild(style);
33
- }
34
-
35
- export function configToIndex(
36
- config: Configuration,
37
- elements: Map<string, HTMLElement>
38
- ) {
39
- const container = elements.get(config.id);
40
- if (!container) {
41
- error(`Element with id ${config.id} not found`);
42
- return [];
43
- }
44
-
45
- return [
46
- index({
47
- indexName: config.indexName,
48
- indexId: config.id,
49
- }).addWidgets(
50
- config.children.flatMap((child) => childToWidget(child, container))
51
- ),
52
- ];
53
- }
54
-
55
- const hitWidgets = new Set<TemplateWidgetTypes>([
56
- 'ais.hits',
57
- 'ais.infiniteHits',
58
- 'ais.frequentlyBoughtTogether',
59
- 'ais.lookingSimilar',
60
- 'ais.relatedProducts',
61
- 'ais.trendingItems',
62
- ]);
63
- function isTemplateWidget(
64
- child: Child
65
- ): child is Child & { children: TemplateChild[] } {
66
- return hitWidgets.has(child.type as any);
67
- }
68
-
69
- const panelWidgets = new Set<PanelWidgetTypes>([
70
- 'ais.refinementList',
71
- 'ais.menu',
72
- 'ais.hierarchicalMenu',
73
- 'ais.breadcrumb',
74
- 'ais.numericMenu',
75
- 'ais.rangeInput',
76
- 'ais.rangeSlider',
77
- 'ais.ratingMenu',
78
- 'ais.toggleRefinement',
79
- ]);
80
- function isPanelWidget(child: Child): child is PanelWidget {
81
- return panelWidgets.has(child.type as any);
82
- }
83
-
84
- const tagNames = new Map<string, string>(
85
- Object.entries({
86
- paragraph: 'p',
87
- span: 'span',
88
- h2: 'h2',
89
- div: 'div',
90
- link: 'a',
91
- image: 'img',
92
- })
93
- );
94
-
95
- function renderText(text: TemplateText[number], hit: any, components: any) {
96
- if (text.type === 'string') {
97
- return text.value;
98
- }
99
-
100
- if (text.type === 'attribute') {
101
- return getPropertyByPath(hit, text.path);
102
- }
103
-
104
- if (text.type === 'highlight') {
105
- return components.Highlight({
106
- hit,
107
- attribute: text.path,
108
- });
109
- }
110
-
111
- if (text.type === 'snippet') {
112
- return components.Snippet({
113
- hit,
114
- attribute: text.path,
115
- });
116
- }
117
-
118
- return null;
119
- }
120
-
121
- function renderAttribute(text: TemplateAttribute[number], hit: any) {
122
- if (text.type === 'string') {
123
- return text.value;
124
- }
125
-
126
- if (text.type === 'attribute') {
127
- return getPropertyByPath(hit, text.path);
128
- }
129
-
130
- return null;
131
- }
132
-
133
- function childToWidget(child: Child, container: HTMLElement): Widget[] {
134
- const widgetContainer = container.appendChild(document.createElement('div'));
135
-
136
- if (child.type === 'columns') {
137
- widgetContainer.classList.add('ais-Columns');
138
-
139
- return child.children
140
- .map((column) => {
141
- const columnContainer = widgetContainer.appendChild(
142
- Object.assign(document.createElement('div'), {
143
- className: 'ais-Column',
144
- })
145
- );
146
- return column.map((ch) => childToWidget(ch, columnContainer));
147
- })
148
- .flat(2);
149
- }
150
-
151
- if (child.type === 'ais.configure') {
152
- return [widgets[child.type]({ ...child.parameters })];
153
- }
154
-
155
- if (isTemplateWidget(child)) {
156
- // type cast is needed here because the spread adding `container` and `templates` loses the type discriminant
157
- const parameters = child.parameters as Parameters<
158
- typeof widgets['ais.hits']
159
- >[0];
160
- const widget = widgets[child.type] as typeof widgets['ais.hits'];
161
-
162
- return [
163
- widget({
164
- ...parameters,
165
- container: widgetContainer,
166
- templates: {
167
- item: (hit: any, { components }) => {
168
- if (!child.children.length) {
169
- return <code> no item template given</code>;
170
- }
171
-
172
- function renderChild(ch: TemplateChild) {
173
- const Tag = tagNames.get(ch.type) as keyof JSX.IntrinsicElements;
174
- if (!Tag) {
175
- return <Fragment></Fragment>;
176
- }
177
-
178
- let children: ComponentChildren = null;
179
- if ('text' in ch.parameters) {
180
- children = ch.parameters.text.map((text) =>
181
- renderText(text, hit, components)
182
- );
183
- } else if ('children' in ch) {
184
- children = ch.children.map(renderChild);
185
- }
186
-
187
- const attributes = Object.fromEntries(
188
- Object.entries(ch.parameters)
189
- .filter(
190
- (tuple): tuple is [string, TemplateAttribute] =>
191
- tuple[0] !== 'text'
192
- )
193
- .map(([key, value]) => [
194
- key,
195
- value.map((item) => renderAttribute(item, hit)).join(''),
196
- ])
197
- );
198
-
199
- return <Tag {...attributes}>{children}</Tag>;
200
- }
201
-
202
- return child.children.map(renderChild);
203
- },
204
- },
205
- }),
206
- ];
207
- }
208
-
209
- if (isPanelWidget(child)) {
210
- // type cast is needed here because the spread adding `container` loses the type discriminant
211
- const {
212
- header,
213
- collapsed: defaultCollapsed,
214
- ...parameters
215
- } = child.parameters as Parameters<
216
- typeof widgets['ais.refinementList']
217
- >[0] & { header: string; collapsed: boolean };
218
- const widget = widgets[child.type] as typeof widgets['ais.refinementList'];
219
- return [
220
- panel<typeof widgets['ais.refinementList']>({
221
- templates: {
222
- header,
223
- collapseButtonText: ({ collapsed }) => (
224
- // @TODO: put this style in a stylesheet
225
- <span style="cursor: pointer">{collapsed ? '+' : '-'}</span>
226
- ),
227
- },
228
- collapsed:
229
- typeof defaultCollapsed === 'undefined'
230
- ? undefined
231
- : () => defaultCollapsed,
232
- })(widget)({
233
- ...parameters,
234
- container: widgetContainer,
235
- }),
236
- ];
237
- }
238
-
239
- // type cast is needed here because the spread adding `container` loses the type discriminant
240
- const parameters = child.parameters as Parameters<
241
- typeof widgets['ais.pagination']
242
- >[0];
243
- const widget = widgets[child.type] as typeof widgets['ais.pagination'];
244
- return [
245
- widget({
246
- ...parameters,
247
- container: widgetContainer,
248
- }),
249
- ];
250
- }
@@ -1,40 +0,0 @@
1
- /** @jsx h */
2
- import algoliasearch from 'algoliasearch/lite';
3
- import InstantSearch from 'instantsearch.js/es/lib/InstantSearch';
4
-
5
- import { fakeFetchConfiguration } from './fake-configuration';
6
- import { getElements, getSettings } from './get-information';
7
- import { configToIndex, injectStyles } from './render';
8
- import { error } from './util';
9
-
10
- declare global {
11
- interface Window {
12
- __search: InstantSearch;
13
- }
14
- }
15
-
16
- export function setupInstantSearch() {
17
- try {
18
- const settings = getSettings();
19
-
20
- const searchClient = algoliasearch(settings.appId, settings.apiKey);
21
- const search = new InstantSearch({
22
- searchClient,
23
- });
24
- window.__search = search;
25
-
26
- const elements = getElements();
27
-
28
- injectStyles();
29
-
30
- fakeFetchConfiguration([...elements.keys()]).then((configuration) => {
31
- search
32
- .addWidgets(
33
- configuration.flatMap((config) => configToIndex(config, elements))
34
- )
35
- .start();
36
- });
37
- } catch (err) {
38
- error((err as Error).message);
39
- }
40
- }