algolia-experiences 1.0.0 → 1.0.1

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,7 +1,7 @@
1
1
  {
2
2
  "name": "algolia-experiences",
3
3
  "license": "MIT",
4
- "version": "1.0.0",
4
+ "version": "1.0.1",
5
5
  "main": "src/index.ts",
6
6
  "jsdelivr": "dist/algolia-experiences.production.min.js",
7
7
  "unpkg": "dist/algolia-experiences.production.min.js",
@@ -17,6 +17,7 @@ const CONFIGURATION_OBJECT: Record<string, Configuration> = {
17
17
  type: 'ais.refinementList',
18
18
  parameters: {
19
19
  attribute: 'brand',
20
+ header: 'Brand',
20
21
  },
21
22
  },
22
23
  ],
@@ -26,7 +27,7 @@ const CONFIGURATION_OBJECT: Record<string, Configuration> = {
26
27
  parameters: {},
27
28
  children: [
28
29
  {
29
- type: 'div',
30
+ type: 'span',
30
31
  parameters: {
31
32
  text: [
32
33
  { type: 'string', value: 'cols: ' },
@@ -50,7 +51,7 @@ const CONFIGURATION_OBJECT: Record<string, Configuration> = {
50
51
  parameters: {},
51
52
  children: [
52
53
  {
53
- type: 'div',
54
+ type: 'span',
54
55
  parameters: {
55
56
  text: [
56
57
  { type: 'string', value: 'one: ' },
@@ -77,7 +78,7 @@ const CONFIGURATION_OBJECT: Record<string, Configuration> = {
77
78
  parameters: {},
78
79
  children: [
79
80
  {
80
- type: 'div',
81
+ type: 'span',
81
82
  parameters: {
82
83
  text: [
83
84
  { type: 'string', value: 'other: ' },
@@ -98,7 +99,7 @@ const CONFIGURATION_OBJECT: Record<string, Configuration> = {
98
99
  parameters: { limit: 4 },
99
100
  children: [
100
101
  {
101
- type: 'div',
102
+ type: 'span',
102
103
  parameters: {
103
104
  text: [
104
105
  { type: 'string', value: 'trending: ' },
@@ -117,6 +118,158 @@ const CONFIGURATION_OBJECT: Record<string, Configuration> = {
117
118
  },
118
119
  ],
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
+ },
120
273
  };
121
274
  const CONFIGURATION_MAP = new Map<string, Configuration>(
122
275
  Object.entries(CONFIGURATION_OBJECT)
@@ -0,0 +1,29 @@
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 ADDED
@@ -0,0 +1,250 @@
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,20 +1,11 @@
1
+ /** @jsx h */
1
2
  import algoliasearch from 'algoliasearch/lite';
2
3
  import InstantSearch from 'instantsearch.js/es/lib/InstantSearch';
3
- import { getPropertyByPath } from 'instantsearch.js/es/lib/utils';
4
4
 
5
5
  import { fakeFetchConfiguration } from './fake-configuration';
6
- import { widgets } from './widgets';
7
-
8
- import type {
9
- Child,
10
- Configuration,
11
- TemplateChild,
12
- TemplateText,
13
- } from './types';
14
- import type { Widget } from 'instantsearch.js';
15
-
16
- // @TODO: hook up to some way it can be set runtime, maybe query params
17
- const VERBOSE = true;
6
+ import { getElements, getSettings } from './get-information';
7
+ import { configToIndex, injectStyles } from './render';
8
+ import { error } from './util';
18
9
 
19
10
  declare global {
20
11
  interface Window {
@@ -47,213 +38,3 @@ export function setupInstantSearch() {
47
38
  error((err as Error).message);
48
39
  }
49
40
  }
50
-
51
- function injectStyles() {
52
- const style = document.createElement('style');
53
- style.textContent = `
54
- .ais-Columns {
55
- display: grid;
56
- grid-template-columns: minmax(min-content, 200px) 1fr;
57
- }
58
- `;
59
- document.head.appendChild(style);
60
- }
61
-
62
- function getSettings(): { appId: string; apiKey: string } {
63
- const metaConfiguration = document.querySelector<HTMLMetaElement>(
64
- 'meta[name="instantsearch-configuration"]'
65
- );
66
-
67
- if (!metaConfiguration || !metaConfiguration.content) {
68
- throw new Error('No meta tag found');
69
- }
70
-
71
- const { appId, apiKey } = JSON.parse(metaConfiguration.content);
72
-
73
- if (!appId || !apiKey) {
74
- throw new Error('Missing appId or apiKey in the meta tag');
75
- }
76
-
77
- return { appId, apiKey };
78
- }
79
-
80
- function getElements() {
81
- const elements = new Map<string, HTMLElement>();
82
- document
83
- .querySelectorAll<HTMLElement>('[data-instantsearch-id]')
84
- .forEach((element) => {
85
- const id = element.dataset.instantsearchId!;
86
- elements.set(id, element);
87
- });
88
-
89
- return elements;
90
- }
91
-
92
- function configToIndex(
93
- config: Configuration,
94
- elements: Map<string, HTMLElement>
95
- ) {
96
- const container = elements.get(config.id);
97
- if (!container) {
98
- error(`Element with id ${config.id} not found`);
99
- return [];
100
- }
101
-
102
- return [
103
- widgets['ais.index']({
104
- indexName: config.indexName,
105
- indexId: config.id,
106
- }).addWidgets(
107
- config.children.flatMap((child) => childToWidget(child, container))
108
- ),
109
- ];
110
- }
111
-
112
- const hitWidgets = new Set([
113
- 'ais.hits',
114
- 'ais.infiniteHits',
115
- 'ais.frequentlyBoughtTogether',
116
- 'ais.lookingSimilar',
117
- 'ais.relatedProducts',
118
- 'ais.trendingItems',
119
- ]);
120
-
121
- function isTemplateWidget(
122
- child: Child
123
- ): child is Child & { children: TemplateChild[] } {
124
- return hitWidgets.has(child.type);
125
- }
126
-
127
- const textChildrenObject = {
128
- paragraph: 'p',
129
- div: 'div',
130
- span: 'span',
131
- h2: 'h2',
132
- };
133
- const textChildren = new Map(Object.entries(textChildrenObject));
134
-
135
- type TextChildType = keyof typeof textChildrenObject;
136
- function isTextChild(child: TemplateChild): child is TemplateChild & {
137
- type: TextChildType;
138
- } {
139
- return textChildren.has(child.type as any);
140
- }
141
-
142
- function renderText(text: TemplateText[number], hit: any, components: any) {
143
- if (text.type === 'string') {
144
- return text.value;
145
- }
146
-
147
- if (text.type === 'attribute') {
148
- return getPropertyByPath(hit, text.path);
149
- }
150
-
151
- if (text.type === 'highlight') {
152
- return components.Highlight({
153
- hit,
154
- attribute: text.path,
155
- });
156
- }
157
-
158
- if (text.type === 'snippet') {
159
- return components.Snippet({
160
- hit,
161
- attribute: text.path,
162
- });
163
- }
164
-
165
- return null;
166
- }
167
-
168
- function renderAttribute(text: TemplateText[number], hit: any) {
169
- if (text.type === 'string') {
170
- return text.value;
171
- }
172
-
173
- if (text.type === 'attribute') {
174
- return getPropertyByPath(hit, text.path);
175
- }
176
-
177
- return null;
178
- }
179
-
180
- function childToWidget(child: Child, container: HTMLElement): Widget[] {
181
- const widgetContainer = container.appendChild(document.createElement('div'));
182
-
183
- if (child.type === 'columns') {
184
- widgetContainer.classList.add('ais-Columns');
185
-
186
- return child.children
187
- .map((column) => column.map((ch) => childToWidget(ch, widgetContainer)))
188
- .flat(2);
189
- }
190
-
191
- if (child.type === 'ais.configure') {
192
- return [widgets[child.type]({ ...child.parameters })];
193
- }
194
-
195
- if (isTemplateWidget(child)) {
196
- // type cast is needed here because the spread adding `container` and `templates` loses the type discriminant
197
- const parameters = child.parameters as Parameters<
198
- typeof widgets['ais.hits']
199
- >[0];
200
- const widget = widgets[child.type] as typeof widgets['ais.hits'];
201
-
202
- return [
203
- widget({
204
- ...parameters,
205
- container: widgetContainer,
206
- templates: {
207
- item: (hit: any, { html, components }) => {
208
- if (!child.children.length) {
209
- return html`<code> no item template given</code>`;
210
- }
211
-
212
- return child.children.map((ch) => {
213
- if (isTextChild(ch)) {
214
- const Tag = textChildren.get(ch.type)!;
215
- return html`<${Tag}>
216
- ${ch.parameters.text.map((text) =>
217
- renderText(text, hit, components)
218
- )}
219
- </${Tag}>`;
220
- }
221
-
222
- if (ch.type === 'image') {
223
- return html`<img
224
- src="${ch.parameters.src
225
- .map((src) => renderAttribute(src, hit))
226
- .join('')}"
227
- alt="${ch.parameters.alt
228
- .map((alt) => renderAttribute(alt, hit))
229
- .join('')}"
230
- />`;
231
- }
232
-
233
- return html``;
234
- });
235
- },
236
- },
237
- }),
238
- ];
239
- }
240
-
241
- // type cast is needed here because the spread adding `container` loses the type discriminant
242
- const parameters = child.parameters as Parameters<
243
- typeof widgets['ais.pagination']
244
- >[0];
245
- const widget = widgets[child.type] as typeof widgets['ais.pagination'];
246
- return [
247
- widget({
248
- ...parameters,
249
- container: widgetContainer,
250
- }),
251
- ];
252
- }
253
-
254
- function error(message: string) {
255
- if (VERBOSE) {
256
- // eslint-disable-next-line no-console
257
- console.error(`[InstantSearch] ${message}`);
258
- }
259
- }
package/src/types.ts CHANGED
@@ -5,42 +5,92 @@ type Attribute = { type: 'attribute'; path: string[] };
5
5
  type Highlight = { type: 'highlight' | 'snippet'; path: string[] };
6
6
  export type TemplateText = Array<Attribute | StaticString | Highlight>;
7
7
  export type TemplateAttribute = Array<Attribute | StaticString>;
8
+ type RegularParameters = {
9
+ class?: TemplateAttribute;
10
+ };
8
11
  export type TemplateChild =
9
12
  | {
10
- type: 'paragraph' | 'div' | 'span' | 'h2';
13
+ type: 'paragraph' | 'span' | 'h2';
11
14
  parameters: {
12
15
  text: TemplateText;
13
- };
16
+ } & RegularParameters;
17
+ }
18
+ | {
19
+ type: 'div';
20
+ parameters: RegularParameters;
21
+ children: TemplateChild[];
14
22
  }
15
23
  | {
16
24
  type: 'image';
17
25
  parameters: {
18
26
  src: TemplateAttribute;
19
27
  alt: TemplateAttribute;
20
- };
28
+ } & RegularParameters;
29
+ }
30
+ | {
31
+ type: 'link';
32
+ parameters: {
33
+ href: TemplateAttribute;
34
+ } & RegularParameters;
35
+ children: TemplateChild[];
36
+ };
37
+
38
+ export type TemplateWidgetTypes =
39
+ | 'ais.hits'
40
+ | 'ais.infiniteHits'
41
+ | 'ais.frequentlyBoughtTogether'
42
+ | 'ais.lookingSimilar'
43
+ | 'ais.relatedProducts'
44
+ | 'ais.trendingItems';
45
+
46
+ export type TemplateWidget<
47
+ TKeys extends TemplateWidgetTypes = TemplateWidgetTypes
48
+ > = {
49
+ [key in TKeys]: {
50
+ type: key;
51
+ parameters: Omit<
52
+ Parameters<typeof widgets[key]>[0],
53
+ 'container' | 'templates'
54
+ >;
55
+ children: TemplateChild[];
56
+ };
57
+ }[TKeys];
58
+
59
+ export type PanelWidgetTypes =
60
+ | 'ais.refinementList'
61
+ | 'ais.menu'
62
+ | 'ais.hierarchicalMenu'
63
+ | 'ais.breadcrumb'
64
+ | 'ais.numericMenu'
65
+ | 'ais.rangeInput'
66
+ | 'ais.rangeSlider'
67
+ | 'ais.ratingMenu'
68
+ | 'ais.toggleRefinement';
69
+ export type PanelWidget<TKeys extends PanelWidgetTypes = PanelWidgetTypes> = {
70
+ [key in TKeys]: {
71
+ type: key;
72
+ parameters: Omit<Parameters<typeof widgets[key]>[0], 'container'> & {
73
+ header?: string;
74
+ collapsed?: boolean;
75
+ };
76
+ };
77
+ }[TKeys];
78
+
79
+ type RegularWidget<TKeys extends keyof typeof widgets = keyof typeof widgets> =
80
+ {
81
+ [key in TKeys]: {
82
+ type: key;
83
+ parameters: Omit<Parameters<typeof widgets[key]>[0], 'container'>;
21
84
  };
85
+ }[TKeys];
22
86
 
23
87
  export type Child =
24
88
  | {
25
- [key in keyof typeof widgets]: key extends
26
- | 'ais.hits'
27
- | 'ais.infiniteHits'
28
- | 'ais.frequentlyBoughtTogether'
29
- | 'ais.lookingSimilar'
30
- | 'ais.relatedProducts'
31
- | 'ais.trendingItems'
32
- ? {
33
- type: key;
34
- parameters: Omit<
35
- Parameters<typeof widgets[key]>[0],
36
- 'container' | 'templates'
37
- >;
38
- children: TemplateChild[];
39
- }
40
- : {
41
- type: key;
42
- parameters: Omit<Parameters<typeof widgets[key]>[0], 'container'>;
43
- };
89
+ [key in keyof typeof widgets]: key extends TemplateWidgetTypes
90
+ ? TemplateWidget<key>
91
+ : key extends PanelWidgetTypes
92
+ ? PanelWidget<key>
93
+ : RegularWidget<key>;
44
94
  }[keyof typeof widgets]
45
95
  | {
46
96
  type: 'columns';
package/src/util.ts ADDED
@@ -0,0 +1,9 @@
1
+ // @TODO: hook up to some way it can be set runtime, maybe query params
2
+ const VERBOSE = true;
3
+
4
+ export function error(message: string) {
5
+ if (VERBOSE) {
6
+ // eslint-disable-next-line no-console
7
+ console.error(`[InstantSearch] ${message}`);
8
+ }
9
+ }
package/src/widgets.ts CHANGED
@@ -7,13 +7,11 @@ import {
7
7
  hierarchicalMenu,
8
8
  hits,
9
9
  hitsPerPage,
10
- index,
11
10
  infiniteHits,
12
11
  lookingSimilar,
13
12
  menu,
14
13
  numericMenu,
15
14
  pagination,
16
- panel,
17
15
  rangeInput,
18
16
  rangeSlider,
19
17
  ratingMenu,
@@ -35,13 +33,11 @@ export const widgets = {
35
33
  'ais.hierarchicalMenu': hierarchicalMenu,
36
34
  'ais.hits': hits,
37
35
  'ais.hitsPerPage': hitsPerPage,
38
- 'ais.index': index,
39
36
  'ais.infiniteHits': infiniteHits,
40
37
  'ais.lookingSimilar': lookingSimilar,
41
38
  'ais.menu': menu,
42
39
  'ais.numericMenu': numericMenu,
43
40
  'ais.pagination': pagination,
44
- 'ais.panel': panel,
45
41
  'ais.rangeInput': rangeInput,
46
42
  'ais.rangeSlider': rangeSlider,
47
43
  'ais.ratingMenu': ratingMenu,