Skip to content

Commit

Permalink
PLANET-7635 Move remaining code from plugin
Browse files Browse the repository at this point in the history
This is in preparation to finally retire it
  • Loading branch information
mleray committed Dec 19, 2024
1 parent c493618 commit 3af9134
Show file tree
Hide file tree
Showing 23 changed files with 1,154 additions and 15 deletions.
187 changes: 187 additions & 0 deletions assets/src/block-editor/AssignOnlyFlatTermSelector/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/**
* The logic of this component was copied from https://github.com/WordPress/gutenberg/blob/master/packages/editor/src/components/post-taxonomies/flat-term-selector.js
* initially, then the functionality of creating non-existing terms was removed from it.
*/

/**
* WordPress dependencies
*/
import {useEffect, useMemo, useState} from '@wordpress/element';
import {FormTokenField, withFilters} from '@wordpress/components';
import {useSelect, useDispatch} from '@wordpress/data';
import {store as coreStore} from '@wordpress/core-data';
import {useDebounce} from '@wordpress/compose';
import {decodeEntities} from '@wordpress/html-entities';

const {__, _x, sprintf} = wp.i18n;

/**
* Shared reference to an empty array for cases where it is important to avoid
* returning a new array reference on every invocation.
*
* @type {Array<any>}
*/
const EMPTY_ARRAY = [];

/**
* Module constants
*/
const MAX_TERMS_SUGGESTIONS = 20;
const DEFAULT_QUERY = {
per_page: MAX_TERMS_SUGGESTIONS,
_fields: 'id,name',
context: 'view',
};

const isSameTermName = (termA, termB) =>
decodeEntities(termA).toLowerCase() ===
decodeEntities(termB).toLowerCase();

const termNamesToIds = (names, terms) => names.map(
termName => terms.find(term => isSameTermName(term.name, termName)).id
);

export const AssignOnlyFlatTermSelector = ({slug}) => {
const [values, setValues] = useState([]);
const [search, setSearch] = useState('');
const debouncedSearch = useDebounce(setSearch, 500);

const {
terms,
taxonomy,
hasAssignAction,
hasResolvedTerms,
} = useSelect(
select => {
const {getCurrentPost, getEditedPostAttribute} = select('core/editor');
const {getEntityRecords, getTaxonomy, hasFinishedResolution} = select(coreStore);
const post = getCurrentPost();
const _taxonomy = getTaxonomy(slug);
const _termIds = _taxonomy ? getEditedPostAttribute(_taxonomy.rest_base) : EMPTY_ARRAY;

const query = {
...DEFAULT_QUERY,
include: _termIds.join(','),
per_page: -1,
};

return {
hasAssignAction: _taxonomy ? post._links?.['wp:action-assign-' + _taxonomy.rest_base] ?? false : false,
taxonomy: _taxonomy,
termIds: _termIds,
terms: _termIds.length ? getEntityRecords('taxonomy', slug, query) : EMPTY_ARRAY,
hasResolvedTerms: hasFinishedResolution('getEntityRecords', ['taxonomy', slug, query]),
};
},
[slug]
);

const {searchResults} = useSelect(
select => {
const {getEntityRecords} = select(coreStore);

return {
searchResults: !!search ? getEntityRecords('taxonomy', slug, {
...DEFAULT_QUERY,
search,
}) : EMPTY_ARRAY,
};
},
[search, slug]
);

// Update terms state only after the selectors are resolved.
// We're using this to avoid terms temporarily disappearing on slow networks
// while core data makes REST API requests.
useEffect(() => {
if (hasResolvedTerms) {
const newValues = (terms ?? []).map(term =>
decodeEntities(term.name)
);

setValues(newValues);
}
}, [terms, hasResolvedTerms]);

const suggestions = useMemo(() => {
return (searchResults ?? []).map(term =>
decodeEntities(term.name)
);
}, [searchResults]);

const {editPost} = useDispatch('core/editor');

if (!hasAssignAction) {
return null;
}

function onUpdateTerms(newTermIds) {
editPost({[taxonomy.rest_base]: newTermIds});
}

function onChange(termNames) {
const availableTerms = [
...(terms ?? []),
...(searchResults ?? []),
];

const uniqueTerms = termNames.reduce((acc, name) => {
if (
!acc.some(n => n.toLowerCase() === name.toLowerCase())
) {
acc.push(name);
}
return acc;
}, []);

// Filter to remove new terms since we don't allow creation.
const allowedTerms = uniqueTerms.filter(
termName => availableTerms.find(term => isSameTermName(term.name, termName))
);

setValues(allowedTerms);

return onUpdateTerms(termNamesToIds(allowedTerms, availableTerms));
}

const newTermLabel =
taxonomy?.labels?.add_new_item ??
(slug === 'post_tag' ? __('Add new tag') : __('Add new Term'));
const singularName =
taxonomy?.labels?.singular_name ??
(slug === 'post_tag' ? __('Tag') : __('Term'));
const termAddedLabel = sprintf(
/* translators: %s: term name. */
_x('%s added', 'term'),
singularName
);
const termRemovedLabel = sprintf(
/* translators: %s: term name. */
_x('%s removed', 'term'),
singularName
);
const removeTermLabel = sprintf(
/* translators: %s: term name. */
_x('Remove %s', 'term'),
singularName
);

return (
<FormTokenField
__next40pxDefaultSize
value={values}
suggestions={suggestions}
onChange={onChange}
onInputChange={debouncedSearch}
maxSuggestions={MAX_TERMS_SUGGESTIONS}
label={newTermLabel}
messages={{
added: termAddedLabel,
removed: termRemovedLabel,
remove: removeTermLabel,
}}
/>
);
};

export default withFilters('editor.PostTaxonomyType')(AssignOnlyFlatTermSelector);
57 changes: 57 additions & 0 deletions assets/src/block-editor/BlockFilters/ImageBlockEdit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Return an editable image block
*
* - display image credits in caption during edition
*
* @param {Object} BlockEdit
* @return {Object} interface to edit images on the Editor
*/
export const ImageBlockEdit = BlockEdit => props => {
if ('core/image' !== props.name) {
return <BlockEdit {...props} />;
}

const {attributes, clientId} = props;
const {id, className = ''} = attributes;

// Get image data
const image = wp.data.useSelect(select => id ? select('core').getMedia(id) : null);
const credits = image?.meta?._credit_text;
const captionText = image?.caption?.raw;
// Compile data for insertion
let image_credits = null;
if (credits && credits.length > 0 && (!captionText || !captionText.includes(credits))) {
image_credits = credits.includes('©') ? credits : ${credits}`;
}

const block_id = clientId ? `block-${clientId}` : null;

// Update width and height when sized rounded styles are selected
if (className.includes('is-style-rounded-')) {
const classes = className.split(' ');
const size = classes.find(c => c.includes('is-style-rounded-')).replace('is-style-rounded-', '') || 180;
attributes.width = parseInt(size);
attributes.height = parseInt(size);
}

// Force to use square images when the class `square-*` is added
if (className.includes('square-')) {
const size = className.slice(className.search('square-') + 'square-'.length).split(' ')[0] || 180;
attributes.width = parseInt(size);
attributes.height = parseInt(size);
}

return (
<>
<BlockEdit {...props} />
{block_id && image_credits && (
captionText ?
<style dangerouslySetInnerHTML={{
__html: `#${block_id} figcaption::after { content: " ${image_credits}"; }`,
}}>
</style> :
<figcaption style={{marginTop: -24}}>{image_credits}</figcaption>
)}
</>
);
};
40 changes: 40 additions & 0 deletions assets/src/block-editor/BlockFilters/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const {addFilter} = wp.hooks;

import {ImageBlockEdit} from './ImageBlockEdit';

export const addBlockFilters = () => {
addFileBlockFilter();
addImageBlockFilter();
addGravityFormsBlockFilter();
};

const addFileBlockFilter = () => {
const setDownloadButtonToggleDefaultFalse = (settings, name) => {
if ('core/file' !== name) {
return settings;
}

settings.attributes.showDownloadButton.default = false;

return settings;
};

addFilter('blocks.registerBlockType', 'planet4-master-theme/filters/file', setDownloadButtonToggleDefaultFalse);
};

const addImageBlockFilter = () => addFilter('editor.BlockEdit', 'core/image/edit', ImageBlockEdit);

// Enforce "AJAX" toggle setting enabled by default, on Gravity form block.
const addGravityFormsBlockFilter = () => {
const setAJAXToggleDefaultTrue = (settings, name) => {
if ('gravityforms/form' !== name) {
return settings;
}

settings.attributes.ajax.default = true;

return settings;
};

addFilter('blocks.registerBlockType', 'planet4-master-theme/filters/gravity-form', setAJAXToggleDefaultTrue);
};
2 changes: 1 addition & 1 deletion assets/src/block-editor/QueryLoopBlockExtension/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const setupQueryLoopBlockExtension = () => {

addFilter(
'editor.BlockEdit',
'planet4-blocks/overrides/query-loop-layout',
'planet4-master-theme/overrides/query-loop-layout',
createHigherOrderComponent(
BlockEdit => props => {
const {attributes, setAttributes} = props;
Expand Down
Loading

0 comments on commit 3af9134

Please sign in to comment.