@wordpress/block-library
Version:
Block library for the WordPress editor.
139 lines (138 loc) • 4.07 kB
JavaScript
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { FormTokenField } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { useState, useEffect, useMemo } from '@wordpress/element';
import { useDebounce } from '@wordpress/compose';
/**
* Internal dependencies
*/
import { getEntitiesInfo, mapToIHasNameAndId } from '../../utils';
import { jsx as _jsx } from "react/jsx-runtime";
const EMPTY_ARRAY = [];
const BASE_QUERY = {
order: 'asc',
_fields: 'id,title',
context: 'view'
};
function ParentControl({
parents,
postType,
onChange
}) {
const [search, setSearch] = useState('');
const [value, setValue] = useState(EMPTY_ARRAY);
const [suggestions, setSuggestions] = useState(EMPTY_ARRAY);
const debouncedSearch = useDebounce(setSearch, 250);
const {
searchResults,
searchHasResolved
} = useSelect(select => {
if (!search) {
return {
searchResults: EMPTY_ARRAY,
searchHasResolved: true
};
}
const {
getEntityRecords,
hasFinishedResolution
} = select(coreStore);
const selectorArgs = ['postType', postType, {
...BASE_QUERY,
search,
orderby: 'relevance',
exclude: parents,
per_page: 20
}];
return {
searchResults: getEntityRecords(...selectorArgs),
searchHasResolved: hasFinishedResolution('getEntityRecords', selectorArgs)
};
}, [search, postType, parents]);
const currentParents = useSelect(select => {
if (!parents?.length) {
return EMPTY_ARRAY;
}
const {
getEntityRecords
} = select(coreStore);
return getEntityRecords('postType', postType, {
...BASE_QUERY,
include: parents,
per_page: parents.length
});
}, [parents, postType]);
// Update the `value` state only after the selectors are resolved
// to avoid emptying the input when we're changing parents.
useEffect(() => {
if (!parents?.length) {
setValue(EMPTY_ARRAY);
}
if (!currentParents?.length) {
return;
}
const currentParentsInfo = getEntitiesInfo(mapToIHasNameAndId(currentParents, 'title.rendered'));
// Returns only the existing entity ids. This prevents the component
// from crashing in the editor, when non existing ids are provided.
const sanitizedValue = parents.reduce((accumulator, id) => {
const entity = currentParentsInfo.mapById[id];
if (entity) {
accumulator.push({
id,
value: entity.name
});
}
return accumulator;
}, []);
setValue(sanitizedValue);
}, [parents, currentParents]);
const entitiesInfo = useMemo(() => {
if (!searchResults?.length) {
return EMPTY_ARRAY;
}
return getEntitiesInfo(mapToIHasNameAndId(searchResults, 'title.rendered'));
}, [searchResults]);
// Update suggestions only when the query has resolved.
useEffect(() => {
if (!searchHasResolved) {
return;
}
setSuggestions(entitiesInfo.names);
}, [entitiesInfo.names, searchHasResolved]);
const getIdByValue = (entitiesMappedByName, entity) => {
const id = entity?.id || entitiesMappedByName?.[entity]?.id;
if (id) {
return id;
}
};
const onParentChange = newValue => {
const ids = Array.from(newValue.reduce((accumulator, entity) => {
// Verify that new values point to existing entities.
const id = getIdByValue(entitiesInfo.mapByName, entity);
if (id) {
accumulator.add(id);
}
return accumulator;
}, new Set()));
setSuggestions(EMPTY_ARRAY);
onChange({
parents: ids
});
};
return /*#__PURE__*/_jsx(FormTokenField, {
__next40pxDefaultSize: true,
label: __('Parents'),
value: value,
onInputChange: debouncedSearch,
suggestions: suggestions,
onChange: onParentChange,
__experimentalShowHowTo: false,
__nextHasNoMarginBottom: true
});
}
export default ParentControl;
//# sourceMappingURL=parent-control.js.map