Skip to content

Commit

Permalink
Update field shortcode UI (#352)
Browse files Browse the repository at this point in the history
* Replace shortcode modal with dropdown

Signed-off-by: brookewp <[email protected]>

* Avoid nesting single menu item

Signed-off-by: brookewp <[email protected]>

* Swap out main dropdown for toolbardropdown

Signed-off-by: brookewp <[email protected]>

* Update styles

Signed-off-by: brookewp <[email protected]>

* Add classname prop to refine styles

Signed-off-by: brookewp <[email protected]>

* Update field popover

Signed-off-by: brookewp <[email protected]>

* Potential fix for fatal metadata issue

Signed-off-by: brookewp <[email protected]>

* Correctly provide context to both BlockBindings::get_value and ::execute_query

---------

Signed-off-by: brookewp <[email protected]>
Co-authored-by: chriszarate <[email protected]>
  • Loading branch information
brookewp and chriszarate authored Feb 6, 2025
1 parent d2f7239 commit 64af67d
Show file tree
Hide file tree
Showing 14 changed files with 421 additions and 297 deletions.
13 changes: 7 additions & 6 deletions inc/Editor/DataBinding/FieldShortcode.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public static function init(): void {
add_action( 'the_content', [ __CLASS__, 'render_frontend_fields' ] );
}

private static function get_meta_field_value( array $context, string $field ): string|null {
private static function get_meta_field_value( array $context, string $field ): string|int|null {
$query_results = BlockBindings::execute_query( $context, $field );

if ( isset( $query_results['metadata'][ $field ]['value'] ) ) {
Expand Down Expand Up @@ -46,19 +46,20 @@ public static function render_frontend_fields( string $content ): string {
$status = 'parse-error';
$value = $fallback_value;
} else {
$context = [
'blockName' => $query_data['remoteData']['blockName'],
'queryInput' => $query_data['remoteData']['queryInput'],
];
$block = [
'context' => [
BlockBindings::$context_name => [
'blockName' => $query_data['remoteData']['blockName'],
'queryInput' => $query_data['remoteData']['queryInput'],
],
BlockBindings::$context_name => $context,
],
];
$field = $query_data['selectedField'];
$type = $query_data['type'] ?? 'field';

if ( 'meta' === $type ) {
$value = self::get_meta_field_value( $block['context'], $field );
$value = self::get_meta_field_value( $context, $field );
} else {
$value = BlockBindings::get_value( [ 'field' => $field ], $block );
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

.rdb-field-shortcode_dropdown,
.remote-data-blocks-field-shortcode-dropdown {

.components-popover__content {
width: unset;
min-width: 160px;
max-width: 320px;
}
}

.rdb-field-shortcode_dropdown {

.components-toolbar-group {
display: flex;
flex-direction: column;
border-right: none;

.components-dropdown-menu {

.components-dropdown-menu__toggle {
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
width: 100%;
}
}
}
}

.remote-data-blocks-inline-field {

span.components-menu-item__item {
width: 100%;

.remote-data-blocks-inline-field-choice {

width: 100%;

.components-base-control__field {
display: flex;
flex-direction: row;
justify-content: space-between;
}
}

}
}

Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { BlockControls } from '@wordpress/block-editor';
import { Modal, ToolbarButton, ToolbarGroup } from '@wordpress/components';
import { ToolbarDropdownMenu, ToolbarGroup } from '@wordpress/components';
import { useEffect, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { RichTextFormat, insertObject, WPFormatEditProps } from '@wordpress/rich-text';

import { FieldShortcodeSelectExisting } from './FieldShortcodeSelectExisting';
import { FieldShortcodeSelectMeta } from './FieldShortcodeSelectMeta';
import { FieldShortcodeSelectNew } from './FieldShortcodeSelectNew';
import { useExistingRemoteData } from '../../hooks/useExistingRemoteData';
import {
formatName,
formatTypeSettings,
} from '@/blocks/remote-data-container/components/field-shortcode';
import { FieldShortcodeSelectFieldPopover } from '@/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelectFieldPopover';
import { FieldShortcodeSelectTabs } from '@/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelectTabs';
import { FieldShortcodeSelectField } from '@/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelection';
import { sendTracksEvent } from '@/blocks/remote-data-container/utils/tracks';
import { getBlockDataSourceType } from '@/utils/localized-block-data';
import './FieldShortcode.scss';

function parseDataQuery( dataQuery?: string ): FieldSelection | null {
if ( ! dataQuery ) {
Expand All @@ -26,36 +29,18 @@ function parseDataQuery( dataQuery?: string ): FieldSelection | null {
}
}

interface QueryInput {
blockName: string;
queryInput: RemoteDataQueryInput;
}

export function FieldShortcodeButton( props: WPFormatEditProps ) {
const { onChange, onFocus, value, isObjectActive, activeObjectAttributes, contentRef } = props;
const fieldSelection = parseDataQuery( activeObjectAttributes?.[ 'data-query' ] );

const [ queryInput, setQueryInput ] = useState< QueryInput | null >( null );
const [ showUI, setShowUI ] = useState< boolean >( false );

function onClick() {
setShowUI( ! showUI );
sendTracksEvent( 'remotedatablocks_field_shortcode', { action: 'toolbar_icon_clicked' } );
}

function onClose() {
setShowUI( false );
onFocus();
}

function onSelectItem( config: BlockConfig, data: RemoteDataQueryInput ) {
setQueryInput( {
blockName: config.name,
queryInput: data,
} );
}
useEffect( () => {
if ( isObjectActive ) {
setShowUI( true );
}
}, [ isObjectActive ] );

function updateOrInsertField( data: FieldSelection | null, fieldValue: string ) {
const updateOrInsertField = ( data: FieldSelection | null, fieldValue: string ) => {
const format: RichTextFormat = {
attributes: {
...activeObjectAttributes,
Expand All @@ -65,86 +50,79 @@ export function FieldShortcodeButton( props: WPFormatEditProps ) {
type: formatName,
};

if ( Object.keys( activeObjectAttributes ).length ) {
const replacements = value.replacements.slice();
replacements[ value.start ] = format;

onChange( { ...value, replacements } );
return;
}

onChange( insertObject( value, format ) );
}

function onSelectField( data: FieldSelection, fieldValue: string ) {
onChange(
Object.keys( activeObjectAttributes ).length
? {
...value,
replacements: value.replacements.map( ( replacement, index ) =>
index === value.start ? format : replacement
),
}
: insertObject( value, format )
);
};

const onSelectField = ( data: FieldSelection, fieldValue: string ) => {
updateOrInsertField( data, fieldValue );
onClose();
setShowUI( false );
onFocus();
sendTracksEvent( 'remotedatablocks_field_shortcode', {
action: data.action,
data_source_type: getBlockDataSourceType( data.remoteData?.blockName ),
selection_path: data.selectionPath,
} );
}
};

function resetField( blockName?: string ): void {
const resetField = ( blockName?: string ): void => {
updateOrInsertField( null, 'Unbound field' );
setQueryInput( null );
sendTracksEvent( 'remotedatablocks_field_shortcode', {
action: 'reset_field_shortcode',
data_source_type: getBlockDataSourceType( blockName ),
} );
}
};

useEffect( () => {
if ( isObjectActive ) {
setShowUI( true );
}
}, [ isObjectActive ] );
const remoteData = useExistingRemoteData();

return (
<>
<BlockControls>
<ToolbarGroup>
<ToolbarButton
icon="shortcode"
isActive={ isObjectActive }
onClick={ onClick }
title="Field shortcode"
/>
</ToolbarGroup>
</BlockControls>

{ showUI && ! fieldSelection && (
<Modal
overlayClassName="remote-data-blocks-pattern__selection-modal"
title={ __( 'Field shortcode' ) }
onRequestClose={ onClose }
isFullScreen
>
{ ! queryInput && (
<FieldShortcodeSelectTabs
{ remoteData.length > 0 ? (
<ToolbarDropdownMenu
className="remote-data-blocks-select-new"
icon="shortcode"
label={ __( 'Select block bindings', 'remote-data-blocks' ) }
popoverProps={ { className: 'rdb-field-shortcode_dropdown', offset: 8 } }
>
{ () => (
<ToolbarGroup>
<FieldShortcodeSelectNew onSelectField={ onSelectField } />
<FieldShortcodeSelectExisting onSelectField={ onSelectField } />
<FieldShortcodeSelectMeta onSelectField={ onSelectField } />
</ToolbarGroup>
) }
</ToolbarDropdownMenu>
) : (
<FieldShortcodeSelectNew
onSelectField={ onSelectField }
onSelectItem={ onSelectItem }
/>
) }
{ queryInput && (
<FieldShortcodeSelectField
blockName={ queryInput.blockName }
onSelectField={ ( data, fieldValue ) =>
onSelectField( { ...data, selectionPath: 'select_new_tab' }, fieldValue )
}
queryInput={ queryInput.queryInput }
fieldType="field"
icon="shortcode"
label={ __( 'Select block bindings', 'remote-data-blocks' ) }
popoverProps={ { offset: 8, placement: 'bottom-start' } }
text={ undefined }
/>
) }
</Modal>
) }
</ToolbarGroup>
</BlockControls>

{ showUI && fieldSelection && (
<FieldShortcodeSelectFieldPopover
contentRef={ contentRef }
fieldSelection={ fieldSelection }
formatTypeSettings={ formatTypeSettings }
onClose={ onClose }
onClose={ () => {
setShowUI( false );
onFocus();
} }
onSelectField={ ( data, fieldValue ) =>
onSelectField( { ...data, selectionPath: 'popover' }, fieldValue )
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { __experimentalHeading as Heading } from '@wordpress/components';
import { DropdownMenu, MenuGroup } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { chevronRightSmall } from '@wordpress/icons';

import { FieldSelectionFromAvailableBindings } from '@/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelection';
import { useExistingRemoteData } from '@/blocks/remote-data-container/hooks/useExistingRemoteData';
Expand All @@ -14,30 +16,32 @@ export function FieldShortcodeSelectExisting( props: FieldShortcodeSelectExistin
remoteData => ! remoteData.isCollection
);

if ( remoteDatas.length === 0 ) {
return (
<div className="remote-data-blocks-select-existing">
<p>No existing items.</p>
</div>
);
}

return (
<div className="remote-data-blocks-select-existing">
{ remoteDatas.map( remoteData => (
<div className="remote-data-blocks-existing-item" key={ remoteData.blockName }>
<Heading className="remote-data-blocks-item-heading" level="4">
{ blockConfigs[ remoteData.blockName ]?.settings.title ?? remoteData.blockName }
</Heading>

<FieldSelectionFromAvailableBindings
onSelectField={ ( data, fieldValue ) =>
props.onSelectField( { ...data, selectionPath: 'select_existing_tab' }, fieldValue )
}
remoteData={ remoteData }
/>
</div>
) ) }
</div>
);
return remoteDatas.length > 0 ? (
<DropdownMenu
icon={ chevronRightSmall }
label=""
text={ __( 'Existing items', 'remote-data-blocks' ) }
popoverProps={ {
className: 'remote-data-blocks-field-shortcode-dropdown remote-data-blocks-select-existing',
placement: 'right-start',
offset: 0,
} }
>
{ () =>
remoteDatas.map( remoteData => (
<MenuGroup
key={ remoteData.blockName }
label={ blockConfigs[ remoteData.blockName ]?.settings.title ?? remoteData.blockName }
>
<FieldSelectionFromAvailableBindings
onSelectField={ ( data, fieldValue ) =>
props.onSelectField( { ...data, selectionPath: 'select_existing_tab' }, fieldValue )
}
remoteData={ remoteData }
/>
</MenuGroup>
) )
}
</DropdownMenu>
) : undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
__experimentalHeading as Heading,
Popover,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { WPFormat, useAnchor } from '@wordpress/rich-text';

import { FieldShortcodeSelectField } from '@/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelection';
Expand Down Expand Up @@ -40,7 +41,7 @@ export function FieldShortcodeSelectFieldPopover( props: FieldShortcodeSelectFie
>
<Card style={ { width: '24rem' } }>
<CardHeader>
<Heading level={ 4 }>Select a field to bind</Heading>
<Heading level={ 4 }>{ __( 'Select a field to bind', 'remote-data-blocks' ) }</Heading>
</CardHeader>
<CardBody>
<FieldShortcodeSelectField
Expand All @@ -55,7 +56,7 @@ export function FieldShortcodeSelectFieldPopover( props: FieldShortcodeSelectFie
</CardBody>
<CardFooter>
<Button onClick={ () => props.resetField( remoteData?.blockName ) } isDestructive>
Reset field
{ __( 'Reset field', 'remote-data-blocks' ) }
</Button>
</CardFooter>
</Card>
Expand Down
Loading

0 comments on commit 64af67d

Please sign in to comment.