corvée(dépendances) ajoute Carbon Fields

This commit is contained in:
gcch 2024-08-09 18:45:01 +02:00
commit 62368587e5
459 changed files with 72750 additions and 26 deletions

View file

@ -0,0 +1,315 @@
/**
* External dependencies.
*/
import cx from 'classnames';
import { Component, Fragment } from '@wordpress/element';
import { ToolbarGroup, PanelBody } from '@wordpress/components';
import {
InnerBlocks,
BlockControls,
InspectorControls
} from '@wordpress/editor';
import { withSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import {
get,
map,
find
} from 'lodash';
/**
* Carbon Fields dependencies.
*/
import { getFieldType } from '@carbon-fields/core';
/**
* Internal dependencies.
*/
import './style.scss';
import Field from '../field';
import ServerSideRender from '../server-side-render';
class BlockEdit extends Component {
/**
* Local state.
*
* @type {Object}
*/
state = {
mode: this.props.container.settings.mode,
currentTab: this.props.supportsTabs
? Object.keys( this.props.container.settings.tabs )[ 0 ]
: null
};
/**
* Handles the change of the field's value.
*
* @param {string} fieldId
* @param {mixed} value
* @return {void}
*/
handleFieldChange = ( fieldId, value ) => {
const { attributes, setAttributes } = this.props;
const fieldName = fieldId.replace( /^.+__(.+)?$/, '$1' );
setAttributes( {
data: {
...attributes.data,
[ fieldName ]: value
}
} );
}
/**
* Handles changing of the mode.
*
* @return {void}
*/
handleModeChange = () => {
this.setState( {
mode: this.isInEditMode ? 'preview' : 'edit'
} );
}
/**
* Handles changing on the tabs.
*
* @param {string} tab
* @return {void}
*/
handleTabChange = ( tab ) => {
this.setState( {
currentTab: tab
} );
}
/**
* Returns whether the block is in edit mode.
*
* @return {boolean}
*/
get isInEditMode() {
return this.state.mode === 'edit';
}
/**
* Returns whether the block is in edit mode.
*
* @return {boolean}
*/
get isInPreviewMode() {
return this.state.mode === 'preview';
}
/**
* Renders a field.
*
* @param {Object} field
* @param {number} index
* @return {Object}
*/
renderField = ( field, index ) => {
const {
clientId,
container,
attributes
} = this.props;
const FieldEdit = getFieldType( field.type, 'block' );
if ( ! FieldEdit ) {
return null;
}
const id = `cf-${ clientId }__${ field.base_name }`;
const value = get( attributes.data, field.base_name, field.default_value );
return (
<Field
key={ index }
id={ id }
field={ field }
>
<FieldEdit
id={ id }
containerId={ container.id }
blockId={ clientId }
value={ value }
field={ field }
name={ field.base_name }
onChange={ this.handleFieldChange }
/>
</Field>
);
}
/**
* Renders the fields in tabs.
*
* @param {string[]} fieldNames
* @return {Object[]}
*/
renderTabbedFields( fieldNames ) {
const { fields } = this.props;
return map( fieldNames, ( fieldName, index ) => {
const field = find( fields, [ 'name', fieldName ] );
return this.renderField( field, index );
} );
}
/**
* Renders the fields that aren't in tabs.
*
* @return {Object}
*/
renderNonTabbedFields() {
return (
<div className="cf-block__fields">
{ this.props.fields.map( this.renderField ) }
</div>
);
}
/**
* Render the component.
*
* @return {Object}
*/
render() {
const { currentTab } = this.state;
const {
clientId,
container,
supportsTabs,
supportsPreview,
supportsInnerBlocks
} = this.props;
const innerBlocks = ( ( supportsInnerBlocks && this.isInEditMode ) && (
<div className="cf-block__inner-blocks">
<InnerBlocks
template={ container.settings.inner_blocks.template }
templateLock={ container.settings.inner_blocks.template_lock }
allowedBlocks={ container.settings.inner_blocks.allowed_blocks }
/>
</div>
) );
return (
<Fragment>
{ container.settings.inner_blocks.position === 'above' && innerBlocks }
{ supportsPreview && (
<BlockControls>
<ToolbarGroup label="Options" controls={ [ {
icon: this.isInEditMode
? 'visibility'
: 'hidden',
title: this.isInEditMode
? __( 'Show preview', 'carbon-fields-ui' )
: __( 'Hide preview', 'carbon-fields-ui' ),
onClick: this.handleModeChange
} ] } />
</BlockControls>
) }
{ ( this.isInEditMode && supportsTabs ) && (
<div className="cf-block__tabs">
<ul className="cf-block__tabs-list">
{ map( container.settings.tabs, ( fieldNames, tabName ) => {
const classes = cx(
'cf-block__tabs-item',
{
'cf-block__tabs-item--current': tabName === currentTab
}
);
return (
<li
key={ tabName }
className={ classes }
onClick={ () => this.handleTabChange( tabName ) }
>
{ tabName }
</li>
);
} ) }
</ul>
</div>
) }
{ this.isInEditMode && (
supportsTabs
? (
map( container.settings.tabs, ( fieldNames, tabName ) => {
return (
<div className="cf-block__fields" key={ tabName } hidden={ tabName !== currentTab }>
{ this.renderTabbedFields( fieldNames ) }
</div>
);
} )
)
: (
this.renderNonTabbedFields()
)
) }
{ this.isInPreviewMode && (
<div className="cf-block__preview">
<ServerSideRender clientId={ clientId } />
</div>
) }
{ container.settings.inner_blocks.position === 'below' && innerBlocks }
{ this.isInPreviewMode && (
<InspectorControls>
{
supportsTabs
? (
map( container.settings.tabs, ( fieldNames, tabName ) => {
return (
<PanelBody key={ tabName } title={ tabName }>
<div className="cf-block__fields">
{ this.renderTabbedFields( fieldNames ) }
</div>
</PanelBody>
);
} )
)
: (
<PanelBody title={ __( 'Fields', 'carbon-fields-ui' ) }>
{ this.renderNonTabbedFields() }
</PanelBody>
)
}
</InspectorControls>
) }
</Fragment>
);
}
}
export default withSelect( ( select, { clientId, name } ) => {
const { hasBlockSupport } = select( 'core/blocks' );
const { getBlockRootClientId } = select( 'core/block-editor' );
const {
getContainerDefinitionByBlockName,
getFieldDefinitionsByBlockName
} = select( 'carbon-fields/blocks' );
const rootClientId = getBlockRootClientId( clientId );
return {
container: getContainerDefinitionByBlockName( name ),
fields: getFieldDefinitionsByBlockName( name ),
supportsTabs: hasBlockSupport( name, 'tabs' ),
supportsPreview: hasBlockSupport( name, 'preview' ) && ! rootClientId,
supportsInnerBlocks: hasBlockSupport( name, 'innerBlocks' )
};
} )( BlockEdit );

View file

@ -0,0 +1,51 @@
/* ==========================================================================
Block
========================================================================== */
.cf-block__tabs {
margin-bottom: $size-base * 4;
}
.cf-block__tabs-list {
.wp-block & {
display: flex;
padding: 0;
margin: 0;
list-style: none outside none;
}
}
.cf-block__tabs-item {
padding: $size-base * 2;
margin: 0;
font-family: $wp-font;
font-size: $wp-font-size;
line-height: 1;
cursor: pointer;
&--current {
box-shadow: 0 3px 0 $wp-color-medium-blue;
}
}
.cf-block__fields {
display: flex;
flex-wrap: wrap;
&[hidden] {
display: none;
}
.wp-block & {
margin: $size-base * -2;
}
}
.cf-block__preview {
min-height: 100px;
}
.cf-block__inner-blocks .block-list-appender {
margin-top: 32px;
margin-bottom: 32px;
}

View file

@ -0,0 +1,41 @@
/**
* External dependencies.
*/
import { Component } from '@wordpress/element';
import { InnerBlocks } from '@wordpress/editor';
class BlockSave extends Component {
/**
* Render the component.
*
* @return {null}
*/
render() {
return null;
}
}
/**
* Adds the content of inner blocks to the saved content.
*
* @param {mixed} element
* @param {Object} blockType
* @return {mixed}
*/
function addInnerBlocksContent( element, blockType ) {
if ( ! /^carbon\-fields\/.+$/.test( blockType.name ) ) {
return element;
}
if ( ! blockType.supports.innerBlocks ) {
return element;
}
return (
<InnerBlocks.Content />
);
}
wp.hooks.addFilter( 'blocks.getSaveElement', 'carbon-fields/blocks', addInnerBlocksContent );
export default BlockSave;

View file

@ -0,0 +1,11 @@
/**
* Carbon Fields dependencies.
*/
import { Field, withFilters } from '@carbon-fields/core';
/**
* Internal dependencies.
*/
import './style.scss';
export default withFilters( 'carbon-fields.field-wrapper.block' )( Field );

View file

@ -0,0 +1,47 @@
/* ==========================================================================
Field
========================================================================== */
.cf-field {
.block-editor & {
font-family: $wp-font;
font-size: $wp-font-size;
line-height: $wp-line-height;
color: $gb-dark-gray-500;
padding: $size-base * 2;
min-width: 0;
}
.wp-block-widget-area & {
padding: 6.5px 20px;
}
.edit-post-sidebar .cf-block__fields > & {
padding: $size-base 0 0;
}
.block-editor .cf-complex & {
border-width: 1px 1px 0 0;
border-style: solid;
border-color: $wp-color-gray-light-500;
}
.edit-post-sidebar .cf-complex & {
border-color: $gb-dark-gray-150;
}
}
.cf-field__label {
font-size: 13px;
font-weight: 600;
color: $wp-color-dark-gray;
.block-editor & {
margin-bottom: 4px;
}
.wp-block-widget-area & {
margin-bottom: 6.5px;
}
}

View file

@ -0,0 +1,18 @@
/**
* The external dependencies.
*/
import { __, sprintf } from '@wordpress/i18n';
/**
* Render a notice to inform the user that the field doesn't have
* any options.
*
* @return {React.Element}
*/
const NotSupportedField = ( { type } ) => (
<em>
{ sprintf( __( `Field of type '%s' is not supported in Gutenberg.`, 'carbon-fields-ui' ), [ type ] ) }
</em>
);
export default NotSupportedField;

View file

@ -0,0 +1,154 @@
/**
* External dependencies.
*/
import apiFetch from '@wordpress/api-fetch';
import { Component, RawHTML } from '@wordpress/element';
import { Placeholder, Spinner } from '@wordpress/components';
import { withSelect } from '@wordpress/data';
import { serialize } from '@wordpress/blocks';
import { __, sprintf } from '@wordpress/i18n';
import { isEqual, debounce } from 'lodash';
/**
* This component is slightly modified version of the `ServerSideRender` component
* that comes by default with Gutenberg.
*
* @see https://github.com/WordPress/gutenberg/tree/master/packages/components/src/server-side-render
*/
class ServerSideRender extends Component {
/**
* Local state.
*
* @type {Object}
*/
state = {
response: null
};
/**
* Lifecycle hook.
*
* @return {void}
*/
componentDidMount() {
this.isStillMounted = true;
// Do the initial rendering.
this.fetch( this.props );
// Only debounce once the initial fetch occurs to ensure that the first
// renders show data as soon as possible.
this.fetch = debounce( this.fetch, 500 );
}
/**
* Lifecycle hook.
*
* @return {void}
*/
componentWillUnmount() {
this.isStillMounted = false;
}
/**
* Lifecycle hook.
*
* @param {Object} prevProps
* @return {void}
*/
componentDidUpdate( prevProps ) {
if ( ! isEqual( prevProps, this.props ) ) {
this.fetch( this.props );
}
}
/**
* Fetch the preview of the block.
*
* @param {Object} props
* @return {void}
*/
fetch( props ) {
if ( ! this.isStillMounted ) {
return;
}
if ( null !== this.state.response ) {
this.setState( { response: null } );
}
const { block } = props;
// Store the latest fetch request so that when we process it, we can
// check if it is the current request, to avoid race conditions on slow networks.
const fetchRequest = this.currentFetchRequest = apiFetch( {
method: 'post',
path: '/carbon-fields/v1/block-renderer',
data: {
name: block.name,
content: serialize( [ block ] )
}
} )
.then( ( response ) => {
if ( this.isStillMounted && fetchRequest === this.currentFetchRequest && response && response.rendered ) {
this.setState( {
response: response.rendered
} );
}
} )
.catch( ( error ) => {
if ( this.isStillMounted && fetchRequest === this.currentFetchRequest ) {
this.setState( {
response: {
error: true,
errorMsg: error.message
}
} );
}
} );
}
/**
* Render the component.
*
* @return {Object}
*/
render() {
const { response } = this.state;
const { className } = this.props;
if ( ! response ) {
return (
<Placeholder className={ className }>
<Spinner />
</Placeholder>
);
} else if ( response.error ) {
return (
<Placeholder className={ className }>
{ sprintf( __( 'Error loading block: %s', 'carbon-fields-ui' ), response.errorMsg ) }
</Placeholder>
);
} else if ( ! response.length ) {
return (
<Placeholder className={ className }>
{ __( 'No results found.', 'carbon-fields-ui' ) }
</Placeholder>
);
}
return (
<RawHTML key="html" className={ className }>
{ response }
</RawHTML>
);
}
}
export default withSelect( ( select, { clientId } ) => {
const { getBlock } = select( 'core/block-editor' );
return {
block: getBlock( clientId )
};
} )( ServerSideRender );