Skip to content

Commit

Permalink
feat(js): introduce classNames API (#317)
Browse files Browse the repository at this point in the history
  • Loading branch information
francoischalifour authored Sep 11, 2020
1 parent 523f015 commit 28bb422
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 11 deletions.
2 changes: 1 addition & 1 deletion examples/js/autocomplete.css
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
outline: currentcolor none medium;
}

.aa-Reset {
.aa-ResetButton {
position: absolute;
right: 0;
height: 2.5rem;
Expand Down
49 changes: 39 additions & 10 deletions packages/autocomplete-js/src/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
AutocompleteState as AutocompleteCoreState,
} from '@algolia/autocomplete-core';

import { concatClassNames } from './concatClassNames';
import { debounce } from './debounce';
import { getDropdownPositionStyle } from './getDropdownPositionStyle';
import { getHTMLElement } from './getHTMLElement';
Expand All @@ -24,6 +25,7 @@ export function autocomplete<TItem>({
container,
render: renderDropdown = defaultRender,
dropdownPlacement = 'input-wrapper-width',
classNames = {},
...props
}: AutocompleteOptions<TItem>): AutocompleteApi<TItem> {
const containerElement = getHTMLElement(container);
Expand Down Expand Up @@ -75,16 +77,16 @@ export function autocomplete<TItem>({
});
setProperties(root, {
...autocomplete.getRootProps(),
class: 'aa-Autocomplete',
class: concatClassNames(['aa-Autocomplete', classNames.root]),
});
const formProps = autocomplete.getFormProps({ inputElement: input });
setProperties(form, {
...formProps,
class: 'aa-Form',
class: concatClassNames(['aa-Form', classNames.form]),
});
setProperties(label, {
...autocomplete.getLabelProps(),
class: 'aa-Label',
class: concatClassNames(['aa-Label', classNames.label]),
innerHTML: `<svg
width="20"
height="20"
Expand All @@ -100,22 +102,28 @@ export function autocomplete<TItem>({
/>
</svg>`,
});
setProperties(inputWrapper, { class: 'aa-InputWrapper' });
setProperties(inputWrapper, {
class: ['aa-InputWrapper', classNames.inputWrapper]
.filter(Boolean)
.join(' '),
});
setProperties(input, {
...autocomplete.getInputProps({ inputElement: input }),
class: 'aa-Input',
class: concatClassNames(['aa-Input', classNames.input]),
});
setProperties(completion, {
class: concatClassNames(['aa-Completion', classNames.completion]),
});
setProperties(completion, { class: 'aa-Completion' });
setProperties(resetButton, {
type: 'reset',
textContent: 'x',
onClick: formProps.onReset,
class: 'aa-Reset',
class: concatClassNames(['aa-ResetButton', classNames.resetButton]),
});
setProperties(dropdown, {
...autocomplete.getDropdownProps(),
hidden: true,
class: 'aa-Dropdown',
class: concatClassNames(['aa-Dropdown', classNames.dropdown]),
});

function render(state: AutocompleteCoreState<TItem>) {
Expand Down Expand Up @@ -153,9 +161,18 @@ export function autocomplete<TItem>({
const source = suggestion.source as AutocompleteSource<TItem>;

const section = document.createElement('section');
setProperties(section, {
class: concatClassNames(['aa-Section', classNames.section]),
});

if (source.templates.header) {
const header = document.createElement('header');
setProperties(header, {
class: concatClassNames([
'aa-SectionHeader',
classNames.sectionHeader,
]),
});
renderTemplate(
source.templates.header({ root: header, state }),
header
Expand All @@ -165,11 +182,17 @@ export function autocomplete<TItem>({

if (items.length > 0) {
const menu = document.createElement('ul');
setProperties(menu, autocomplete.getMenuProps());
setProperties(menu, {
...autocomplete.getMenuProps(),
class: concatClassNames(['aa-Menu', classNames.menu]),
});

const menuItems = items.map((item) => {
const li = document.createElement('li');
setProperties(li, autocomplete.getItemProps({ item, source }));
setProperties(li, {
...autocomplete.getItemProps({ item, source }),
class: concatClassNames(['aa-Item', classNames.item]),
});
renderTemplate(source.templates.item({ root: li, item, state }), li);

return li;
Expand All @@ -184,6 +207,12 @@ export function autocomplete<TItem>({

if (source.templates.footer) {
const footer = document.createElement('footer');
setProperties(footer, {
class: concatClassNames([
'aa-SectionFooter',
classNames.sectionFooter,
]),
});
renderTemplate(
source.templates.footer({ root: footer, state }),
footer
Expand Down
3 changes: 3 additions & 0 deletions packages/autocomplete-js/src/concatClassNames.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function concatClassNames(classNames: Array<string | undefined>) {
return classNames.filter(Boolean).join(' ');
}
20 changes: 20 additions & 0 deletions packages/autocomplete-js/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,26 @@ export interface AutocompleteOptions<TItem>
* @default "input-wrapper-width"
*/
dropdownPlacement?: 'start' | 'end' | 'full-width' | 'input-wrapper-width';
/**
* The class names to inject in each created DOM element.
*
* It it useful to design with external CSS frameworks.
*/
classNames?: {
root?: string;
form?: string;
label?: string;
inputWrapper?: string;
input?: string;
completion?: string;
resetButton?: string;
dropdown?: string;
section?: string;
sectionHeader?: string;
menu?: string;
item?: string;
sectionFooter?: string;
};
/**
* Function called to render the autocomplete results. It is useful for rendering sections in different row or column layouts.
* The default implementation appends all the sections to the root:
Expand Down
24 changes: 24 additions & 0 deletions packages/website/docs/autocomplete-js.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,30 @@ import CreateAutocompleteProps from './partials/createAutocomplete-props.md'
The dropdown horizontal position.

### `classNames`

> `ClassNames`
The class names to inject in each created DOM element. It it useful to design with external CSS frameworks.

```ts
type ClassNames = {
root?: string;
form?: string;
label?: string;
inputWrapper?: string;
input?: string;
completion?: string;
resetButton?: string;
dropdown?: string;
section?: string;
sectionHeader?: string;
menu?: string;
item?: string;
sectionFooter?: string;
};
```

### `render`

> `(params: { root: HTMLElement, sections: HTMLElement[], state: AutocompleteState<TItem> }) => void`
Expand Down

0 comments on commit 28bb422

Please sign in to comment.