@shopify/polaris
Version:
Shopify’s product component library
89 lines (88 loc) • 3.84 kB
JavaScript
import React, { useState, useCallback } from 'react';
import { arraysAreEqual } from '../../utilities/arrays';
import { useUniqueId } from '../../utilities/unique-id';
import { useDeepEffect } from '../../utilities/use-deep-effect';
import { Option } from './components';
import styles from './OptionList.scss';
export function OptionList({ options, sections, title, selected, allowMultiple, role, optionRole, onChange, id: idProp, }) {
const [normalizedOptions, setNormalizedOptions] = useState(createNormalizedOptions(options, sections, title));
const id = useUniqueId('OptionList', idProp);
useDeepEffect(() => {
setNormalizedOptions(createNormalizedOptions(options || [], sections || [], title));
}, [options, sections, title], optionArraysAreEqual);
const handleClick = useCallback((sectionIndex, optionIndex) => {
const selectedValue = normalizedOptions[sectionIndex].options[optionIndex].value;
const foundIndex = selected.indexOf(selectedValue);
if (allowMultiple) {
const newSelection = foundIndex === -1
? [selectedValue, ...selected]
: [
...selected.slice(0, foundIndex),
...selected.slice(foundIndex + 1, selected.length),
];
onChange(newSelection);
return;
}
onChange([selectedValue]);
}, [normalizedOptions, selected, allowMultiple, onChange]);
const optionsExist = normalizedOptions.length > 0;
const optionsMarkup = optionsExist
? normalizedOptions.map(({ title, options }, sectionIndex) => {
const titleMarkup = title ? (<p className={styles.Title} role={role}>
{title}
</p>) : null;
const optionsMarkup = options &&
options.map((option, optionIndex) => {
const isSelected = selected.includes(option.value);
const optionId = option.id || `${id}-${sectionIndex}-${optionIndex}`;
return (<Option {...option} key={optionId} id={optionId} section={sectionIndex} index={optionIndex} onClick={handleClick} select={isSelected} allowMultiple={allowMultiple} role={optionRole}/>);
});
return (<li key={title || `noTitle-${sectionIndex}`}>
{titleMarkup}
<ul className={styles.Options} id={`${id}-${sectionIndex}`} role={role} aria-multiselectable={allowMultiple}>
{optionsMarkup}
</ul>
</li>);
})
: null;
return (<ul className={styles.OptionList} role={role}>
{optionsMarkup}
</ul>);
}
function createNormalizedOptions(options, sections, title) {
if (options == null) {
const section = { options: [], title };
return sections == null ? [] : [section, ...sections];
}
if (sections == null) {
return [
{
title,
options,
},
];
}
return [
{
title,
options,
},
...sections,
];
}
function isSection(arr) {
return typeof arr[0] === 'object' && arr[0].hasOwnProperty('options');
}
function optionArraysAreEqual(firstArray, secondArray) {
if (isSection(firstArray) && isSection(secondArray)) {
return arraysAreEqual(firstArray, secondArray, testSectionsPropEquality);
}
return arraysAreEqual(firstArray, secondArray);
}
function testSectionsPropEquality(previousSection, currentSection) {
const { options: previousOptions } = previousSection;
const { options: currentOptions } = currentSection;
const optionsAreEqual = arraysAreEqual(previousOptions, currentOptions);
const titlesAreEqual = previousSection.title === currentSection.title;
return optionsAreEqual && titlesAreEqual;
}