Skip to content

Commit 04bef73

Browse files
feat(js): introduce Props API
1 parent 72d92b1 commit 04bef73

File tree

7 files changed

+166
-31
lines changed

7 files changed

+166
-31
lines changed

packages/autocomplete-js/src/autocomplete.ts

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ export function autocomplete<TItem extends BaseItem>({
2424
render: renderer = defaultRenderer,
2525
panelPlacement = 'input-wrapper-width',
2626
classNames = {},
27+
getEnvironmentProps = ({ props }) => props,
28+
getFormProps = ({ props }) => props,
29+
getInputProps = ({ props }) => props,
30+
getItemProps = ({ props }) => props,
31+
getLabelProps = ({ props }) => props,
32+
getListProps = ({ props }) => props,
33+
getPanelProps = ({ props }) => props,
34+
getRootProps = ({ props }) => props,
2735
...props
2836
}: AutocompleteOptions<TItem>): AutocompleteApi<TItem> {
2937
const { runEffect, cleanupEffects } = createEffectWrapper();
@@ -41,6 +49,16 @@ export function autocomplete<TItem extends BaseItem>({
4149
props.onStateChange?.(options);
4250
},
4351
});
52+
const initialState: AutocompleteState<TItem> = {
53+
collections: [],
54+
completion: null,
55+
context: {},
56+
isOpen: false,
57+
query: '',
58+
selectedItemId: null,
59+
status: 'idle',
60+
...props.initialState,
61+
};
4462

4563
const {
4664
inputWrapper,
@@ -53,8 +71,17 @@ export function autocomplete<TItem extends BaseItem>({
5371
root,
5472
panel,
5573
} = createAutocompleteDom({
56-
...autocomplete,
74+
state: initialState,
75+
autocomplete,
5776
classNames,
77+
getEnvironmentProps,
78+
getFormProps,
79+
getInputProps,
80+
getItemProps,
81+
getLabelProps,
82+
getListProps,
83+
getPanelProps,
84+
getRootProps,
5885
});
5986

6087
function setPanelPosition() {
@@ -92,19 +119,17 @@ export function autocomplete<TItem extends BaseItem>({
92119

93120
runEffect(() => {
94121
const panelRoot = getHTMLElement(panelContainer);
95-
const state: AutocompleteState<TItem> = {
96-
collections: [],
97-
completion: null,
98-
context: {},
99-
isOpen: false,
100-
query: '',
101-
selectedItemId: null,
102-
status: 'idle',
103-
...props.initialState,
104-
};
105122
render(renderer, {
106-
state,
107-
...autocomplete,
123+
state: initialState,
124+
autocomplete,
125+
getEnvironmentProps,
126+
getFormProps,
127+
getInputProps,
128+
getItemProps,
129+
getLabelProps,
130+
getListProps,
131+
getPanelProps,
132+
getRootProps,
108133
classNames,
109134
panelRoot,
110135
root,
@@ -136,7 +161,15 @@ export function autocomplete<TItem extends BaseItem>({
136161
}>(({ state }) => {
137162
unmountRef.current = render(renderer, {
138163
state,
139-
...autocomplete,
164+
autocomplete,
165+
getEnvironmentProps,
166+
getFormProps,
167+
getInputProps,
168+
getItemProps,
169+
getLabelProps,
170+
getListProps,
171+
getPanelProps,
172+
getRootProps,
140173
classNames,
141174
panelRoot,
142175
root,

packages/autocomplete-js/src/components/Input.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
import { AutocompleteApi as AutocompleteCoreApi } from '@algolia/autocomplete-core';
22

3+
import { AutocompletePropGetters, AutocompleteState } from '../types';
34
import { Component, WithClassNames } from '../types/Component';
45
import { concatClassNames, setProperties } from '../utils';
56

67
type InputProps = WithClassNames<{
7-
getInputProps: AutocompleteCoreApi<any>['getInputProps'];
8+
state: AutocompleteState<any>;
9+
getInputProps: AutocompletePropGetters<any>['getInputProps'];
10+
getInputPropsCore: AutocompleteCoreApi<any>['getInputProps'];
811
}>;
912

1013
export const Input: Component<InputProps, HTMLInputElement> = ({
1114
classNames,
1215
getInputProps,
16+
getInputPropsCore,
17+
state,
1318
}) => {
1419
const element = document.createElement('input');
1520
setProperties(element, {
16-
...getInputProps({ inputElement: element }),
21+
...getInputProps({
22+
state,
23+
props: getInputPropsCore({ inputElement: element }),
24+
inputElement: element,
25+
}),
1726
class: concatClassNames(['aa-Input', classNames.input]),
1827
});
1928

packages/autocomplete-js/src/createAutocompleteDom.ts

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,61 @@ import {
1414
Root,
1515
SubmitButton,
1616
} from './components';
17-
import { AutocompleteClassNames, AutocompleteDom } from './types';
17+
import {
18+
AutocompleteClassNames,
19+
AutocompleteDom,
20+
AutocompletePropGetters,
21+
AutocompleteState,
22+
} from './types';
1823

19-
type CreateDomProps<TItem extends BaseItem> = AutocompleteCoreApi<TItem> & {
24+
type CreateDomProps<TItem extends BaseItem> = AutocompletePropGetters<TItem> & {
2025
classNames: Partial<AutocompleteClassNames>;
26+
autocomplete: AutocompleteCoreApi<TItem>;
27+
state: AutocompleteState<TItem>;
2128
};
2229

2330
export function createAutocompleteDom<TItem extends BaseItem>({
31+
autocomplete,
32+
classNames,
2433
getRootProps,
2534
getFormProps,
2635
getLabelProps,
2736
getInputProps,
2837
getPanelProps,
29-
classNames,
38+
state,
3039
}: CreateDomProps<TItem>): AutocompleteDom {
31-
const root = Root({ classNames, ...getRootProps({}) });
40+
const root = Root({
41+
classNames,
42+
...getRootProps({
43+
state,
44+
props: autocomplete.getRootProps({}),
45+
}),
46+
});
3247
const inputWrapper = InputWrapper({ classNames });
33-
const label = Label({ classNames, ...getLabelProps({}) });
34-
const input = Input({ classNames, getInputProps });
48+
const label = Label({
49+
classNames,
50+
...getLabelProps({ state, props: autocomplete.getLabelProps({}) }),
51+
});
52+
const input = Input({
53+
classNames,
54+
state,
55+
getInputProps,
56+
getInputPropsCore: autocomplete.getInputProps,
57+
});
3558
const submitButton = SubmitButton({ classNames });
3659
const resetButton = ResetButton({ classNames });
3760
const loadingIndicator = LoadingIndicator({ classNames });
38-
const form = Form({ classNames, ...getFormProps({ inputElement: input }) });
39-
const panel = Panel({ classNames, ...getPanelProps({}) });
61+
const form = Form({
62+
classNames,
63+
...getFormProps({
64+
state,
65+
props: autocomplete.getFormProps({ inputElement: input }),
66+
}),
67+
});
68+
const panel = Panel({
69+
classNames,
70+
...getPanelProps({ state, props: autocomplete.getPanelProps({}) }),
71+
});
4072

4173
label.appendChild(submitButton);
4274
inputWrapper.appendChild(input);

packages/autocomplete-js/src/render.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { renderTemplate } from './renderTemplate';
1313
import {
1414
AutocompleteClassNames,
1515
AutocompleteDom,
16+
AutocompletePropGetters,
1617
AutocompleteRenderer,
1718
AutocompleteState,
1819
} from './types';
@@ -22,12 +23,14 @@ type RenderProps<TItem extends BaseItem> = {
2223
state: AutocompleteState<TItem>;
2324
classNames: Partial<AutocompleteClassNames>;
2425
panelRoot: HTMLElement;
25-
} & AutocompleteCoreApi<TItem> &
26-
AutocompleteDom;
26+
autocomplete: AutocompleteCoreApi<TItem>;
27+
} & AutocompleteDom &
28+
AutocompletePropGetters<TItem>;
2729

2830
export function render<TItem extends BaseItem>(
2931
renderer: AutocompleteRenderer<TItem>,
3032
{
33+
autocomplete,
3134
state,
3235
getRootProps,
3336
getInputProps,
@@ -43,8 +46,18 @@ export function render<TItem extends BaseItem>(
4346
panel,
4447
}: RenderProps<TItem>
4548
): () => void {
46-
setPropertiesWithoutEvents(root, getRootProps({}));
47-
setPropertiesWithoutEvents(input, getInputProps({ inputElement: input }));
49+
setPropertiesWithoutEvents(
50+
root,
51+
getRootProps({ state, props: autocomplete.getRootProps({}) })
52+
);
53+
setPropertiesWithoutEvents(
54+
input,
55+
getInputProps({
56+
state,
57+
props: autocomplete.getInputProps({ inputElement: input }),
58+
inputElement: input,
59+
})
60+
);
4861
setPropertiesWithoutEvents(resetButton, { hidden: !state.query });
4962
setProperties(submitButton, { hidden: state.status === 'stalled' });
5063
setProperties(loadingIndicator, { hidden: state.status !== 'stalled' });
@@ -88,14 +101,17 @@ export function render<TItem extends BaseItem>(
88101
if (items.length > 0) {
89102
const listElement = SourceList({
90103
classNames,
91-
...getListProps(),
104+
...getListProps({ state, props: autocomplete.getListProps({}) }),
92105
});
93106
const listFragment = document.createDocumentFragment();
94107

95108
items.forEach((item) => {
96109
const itemElement = SourceItem({
97110
classNames,
98-
...getItemProps({ item, source }),
111+
...getItemProps({
112+
state,
113+
props: autocomplete.getItemProps({ item, source }),
114+
}),
99115
});
100116

101117
renderTemplate({

packages/autocomplete-js/src/types/AutocompleteOptions.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
import { MaybePromise } from '@algolia/autocomplete-shared';
77

88
import { AutocompleteClassNames } from './AutocompleteClassNames';
9+
import { AutocompletePropGetters } from './AutocompletePropGetters';
910
import { AutocompleteSource } from './AutocompleteSource';
1011
import { AutocompleteState } from './AutocompleteState';
1112

@@ -16,7 +17,8 @@ export type AutocompleteRenderer<TItem extends BaseItem> = (params: {
1617
}) => void;
1718

1819
export interface AutocompleteOptions<TItem extends BaseItem>
19-
extends AutocompleteCoreOptions<TItem> {
20+
extends AutocompleteCoreOptions<TItem>,
21+
Partial<AutocompletePropGetters<TItem>> {
2022
/**
2123
* The container for the Autocomplete search box.
2224
*
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {
2+
BaseItem,
3+
AutocompleteApi as AutocompleteCoreApi,
4+
} from '@algolia/autocomplete-core';
5+
6+
import { AutocompleteState } from './AutocompleteState';
7+
8+
export type AutocompletePropGetters<TItem extends BaseItem> = {
9+
getEnvironmentProps(params: {
10+
state: AutocompleteState<TItem>;
11+
props: ReturnType<AutocompleteCoreApi<TItem>['getEnvironmentProps']>;
12+
}): ReturnType<AutocompleteCoreApi<TItem>['getEnvironmentProps']>;
13+
getFormProps(params: {
14+
state: AutocompleteState<TItem>;
15+
props: ReturnType<AutocompleteCoreApi<TItem>['getFormProps']>;
16+
}): ReturnType<AutocompleteCoreApi<TItem>['getFormProps']>;
17+
getInputProps(params: {
18+
state: AutocompleteState<TItem>;
19+
props: ReturnType<AutocompleteCoreApi<TItem>['getInputProps']>;
20+
inputElement: HTMLInputElement;
21+
}): ReturnType<AutocompleteCoreApi<TItem>['getInputProps']>;
22+
getItemProps(params: {
23+
state: AutocompleteState<TItem>;
24+
props: ReturnType<AutocompleteCoreApi<TItem>['getItemProps']>;
25+
}): ReturnType<AutocompleteCoreApi<TItem>['getItemProps']>;
26+
getLabelProps(params: {
27+
state: AutocompleteState<TItem>;
28+
props: ReturnType<AutocompleteCoreApi<TItem>['getLabelProps']>;
29+
}): ReturnType<AutocompleteCoreApi<TItem>['getLabelProps']>;
30+
getListProps(params: {
31+
state: AutocompleteState<TItem>;
32+
props: ReturnType<AutocompleteCoreApi<TItem>['getListProps']>;
33+
}): ReturnType<AutocompleteCoreApi<TItem>['getListProps']>;
34+
getPanelProps(params: {
35+
state: AutocompleteState<TItem>;
36+
props: ReturnType<AutocompleteCoreApi<TItem>['getPanelProps']>;
37+
}): ReturnType<AutocompleteCoreApi<TItem>['getPanelProps']>;
38+
getRootProps(params: {
39+
state: AutocompleteState<TItem>;
40+
props: ReturnType<AutocompleteCoreApi<TItem>['getRootProps']>;
41+
}): ReturnType<AutocompleteCoreApi<TItem>['getRootProps']>;
42+
};

packages/autocomplete-js/src/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ export * from './AutocompleteClassNames';
33
export * from './AutocompleteCollection';
44
export * from './AutocompleteDom';
55
export * from './AutocompleteOptions';
6+
export * from './AutocompletePropGetters';
67
export * from './AutocompleteSource';
78
export * from './AutocompleteState';

0 commit comments

Comments
 (0)