Skip to content

Commit 64af67d

Browse files
Update field shortcode UI (#352)
* 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]>
1 parent d2f7239 commit 64af67d

14 files changed

+421
-297
lines changed

inc/Editor/DataBinding/FieldShortcode.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public static function init(): void {
99
add_action( 'the_content', [ __CLASS__, 'render_frontend_fields' ] );
1010
}
1111

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

1515
if ( isset( $query_results['metadata'][ $field ]['value'] ) ) {
@@ -46,19 +46,20 @@ public static function render_frontend_fields( string $content ): string {
4646
$status = 'parse-error';
4747
$value = $fallback_value;
4848
} else {
49+
$context = [
50+
'blockName' => $query_data['remoteData']['blockName'],
51+
'queryInput' => $query_data['remoteData']['queryInput'],
52+
];
4953
$block = [
5054
'context' => [
51-
BlockBindings::$context_name => [
52-
'blockName' => $query_data['remoteData']['blockName'],
53-
'queryInput' => $query_data['remoteData']['queryInput'],
54-
],
55+
BlockBindings::$context_name => $context,
5556
],
5657
];
5758
$field = $query_data['selectedField'];
5859
$type = $query_data['type'] ?? 'field';
5960

6061
if ( 'meta' === $type ) {
61-
$value = self::get_meta_field_value( $block['context'], $field );
62+
$value = self::get_meta_field_value( $context, $field );
6263
} else {
6364
$value = BlockBindings::get_value( [ 'field' => $field ], $block );
6465
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
.rdb-field-shortcode_dropdown,
3+
.remote-data-blocks-field-shortcode-dropdown {
4+
5+
.components-popover__content {
6+
width: unset;
7+
min-width: 160px;
8+
max-width: 320px;
9+
}
10+
}
11+
12+
.rdb-field-shortcode_dropdown {
13+
14+
.components-toolbar-group {
15+
display: flex;
16+
flex-direction: column;
17+
border-right: none;
18+
19+
.components-dropdown-menu {
20+
21+
.components-dropdown-menu__toggle {
22+
display: flex;
23+
flex-direction: row-reverse;
24+
justify-content: space-between;
25+
width: 100%;
26+
}
27+
}
28+
}
29+
}
30+
31+
.remote-data-blocks-inline-field {
32+
33+
span.components-menu-item__item {
34+
width: 100%;
35+
36+
.remote-data-blocks-inline-field-choice {
37+
38+
width: 100%;
39+
40+
.components-base-control__field {
41+
display: flex;
42+
flex-direction: row;
43+
justify-content: space-between;
44+
}
45+
}
46+
47+
}
48+
}
49+

src/blocks/remote-data-container/components/field-shortcode/FieldShortcodeButton.tsx

Lines changed: 59 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import { BlockControls } from '@wordpress/block-editor';
2-
import { Modal, ToolbarButton, ToolbarGroup } from '@wordpress/components';
2+
import { ToolbarDropdownMenu, ToolbarGroup } from '@wordpress/components';
33
import { useEffect, useState } from '@wordpress/element';
44
import { __ } from '@wordpress/i18n';
55
import { RichTextFormat, insertObject, WPFormatEditProps } from '@wordpress/rich-text';
66

7+
import { FieldShortcodeSelectExisting } from './FieldShortcodeSelectExisting';
8+
import { FieldShortcodeSelectMeta } from './FieldShortcodeSelectMeta';
9+
import { FieldShortcodeSelectNew } from './FieldShortcodeSelectNew';
10+
import { useExistingRemoteData } from '../../hooks/useExistingRemoteData';
711
import {
812
formatName,
913
formatTypeSettings,
1014
} from '@/blocks/remote-data-container/components/field-shortcode';
1115
import { FieldShortcodeSelectFieldPopover } from '@/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelectFieldPopover';
12-
import { FieldShortcodeSelectTabs } from '@/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelectTabs';
13-
import { FieldShortcodeSelectField } from '@/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelection';
1416
import { sendTracksEvent } from '@/blocks/remote-data-container/utils/tracks';
1517
import { getBlockDataSourceType } from '@/utils/localized-block-data';
18+
import './FieldShortcode.scss';
1619

1720
function parseDataQuery( dataQuery?: string ): FieldSelection | null {
1821
if ( ! dataQuery ) {
@@ -26,36 +29,18 @@ function parseDataQuery( dataQuery?: string ): FieldSelection | null {
2629
}
2730
}
2831

29-
interface QueryInput {
30-
blockName: string;
31-
queryInput: RemoteDataQueryInput;
32-
}
33-
3432
export function FieldShortcodeButton( props: WPFormatEditProps ) {
3533
const { onChange, onFocus, value, isObjectActive, activeObjectAttributes, contentRef } = props;
3634
const fieldSelection = parseDataQuery( activeObjectAttributes?.[ 'data-query' ] );
37-
38-
const [ queryInput, setQueryInput ] = useState< QueryInput | null >( null );
3935
const [ showUI, setShowUI ] = useState< boolean >( false );
4036

41-
function onClick() {
42-
setShowUI( ! showUI );
43-
sendTracksEvent( 'remotedatablocks_field_shortcode', { action: 'toolbar_icon_clicked' } );
44-
}
45-
46-
function onClose() {
47-
setShowUI( false );
48-
onFocus();
49-
}
50-
51-
function onSelectItem( config: BlockConfig, data: RemoteDataQueryInput ) {
52-
setQueryInput( {
53-
blockName: config.name,
54-
queryInput: data,
55-
} );
56-
}
37+
useEffect( () => {
38+
if ( isObjectActive ) {
39+
setShowUI( true );
40+
}
41+
}, [ isObjectActive ] );
5742

58-
function updateOrInsertField( data: FieldSelection | null, fieldValue: string ) {
43+
const updateOrInsertField = ( data: FieldSelection | null, fieldValue: string ) => {
5944
const format: RichTextFormat = {
6045
attributes: {
6146
...activeObjectAttributes,
@@ -65,86 +50,79 @@ export function FieldShortcodeButton( props: WPFormatEditProps ) {
6550
type: formatName,
6651
};
6752

68-
if ( Object.keys( activeObjectAttributes ).length ) {
69-
const replacements = value.replacements.slice();
70-
replacements[ value.start ] = format;
71-
72-
onChange( { ...value, replacements } );
73-
return;
74-
}
75-
76-
onChange( insertObject( value, format ) );
77-
}
78-
79-
function onSelectField( data: FieldSelection, fieldValue: string ) {
53+
onChange(
54+
Object.keys( activeObjectAttributes ).length
55+
? {
56+
...value,
57+
replacements: value.replacements.map( ( replacement, index ) =>
58+
index === value.start ? format : replacement
59+
),
60+
}
61+
: insertObject( value, format )
62+
);
63+
};
64+
65+
const onSelectField = ( data: FieldSelection, fieldValue: string ) => {
8066
updateOrInsertField( data, fieldValue );
81-
onClose();
67+
setShowUI( false );
68+
onFocus();
8269
sendTracksEvent( 'remotedatablocks_field_shortcode', {
8370
action: data.action,
8471
data_source_type: getBlockDataSourceType( data.remoteData?.blockName ),
8572
selection_path: data.selectionPath,
8673
} );
87-
}
74+
};
8875

89-
function resetField( blockName?: string ): void {
76+
const resetField = ( blockName?: string ): void => {
9077
updateOrInsertField( null, 'Unbound field' );
91-
setQueryInput( null );
9278
sendTracksEvent( 'remotedatablocks_field_shortcode', {
9379
action: 'reset_field_shortcode',
9480
data_source_type: getBlockDataSourceType( blockName ),
9581
} );
96-
}
82+
};
9783

98-
useEffect( () => {
99-
if ( isObjectActive ) {
100-
setShowUI( true );
101-
}
102-
}, [ isObjectActive ] );
84+
const remoteData = useExistingRemoteData();
10385

10486
return (
10587
<>
10688
<BlockControls>
10789
<ToolbarGroup>
108-
<ToolbarButton
109-
icon="shortcode"
110-
isActive={ isObjectActive }
111-
onClick={ onClick }
112-
title="Field shortcode"
113-
/>
114-
</ToolbarGroup>
115-
</BlockControls>
116-
117-
{ showUI && ! fieldSelection && (
118-
<Modal
119-
overlayClassName="remote-data-blocks-pattern__selection-modal"
120-
title={ __( 'Field shortcode' ) }
121-
onRequestClose={ onClose }
122-
isFullScreen
123-
>
124-
{ ! queryInput && (
125-
<FieldShortcodeSelectTabs
90+
{ remoteData.length > 0 ? (
91+
<ToolbarDropdownMenu
92+
className="remote-data-blocks-select-new"
93+
icon="shortcode"
94+
label={ __( 'Select block bindings', 'remote-data-blocks' ) }
95+
popoverProps={ { className: 'rdb-field-shortcode_dropdown', offset: 8 } }
96+
>
97+
{ () => (
98+
<ToolbarGroup>
99+
<FieldShortcodeSelectNew onSelectField={ onSelectField } />
100+
<FieldShortcodeSelectExisting onSelectField={ onSelectField } />
101+
<FieldShortcodeSelectMeta onSelectField={ onSelectField } />
102+
</ToolbarGroup>
103+
) }
104+
</ToolbarDropdownMenu>
105+
) : (
106+
<FieldShortcodeSelectNew
126107
onSelectField={ onSelectField }
127-
onSelectItem={ onSelectItem }
128-
/>
129-
) }
130-
{ queryInput && (
131-
<FieldShortcodeSelectField
132-
blockName={ queryInput.blockName }
133-
onSelectField={ ( data, fieldValue ) =>
134-
onSelectField( { ...data, selectionPath: 'select_new_tab' }, fieldValue )
135-
}
136-
queryInput={ queryInput.queryInput }
137-
fieldType="field"
108+
icon="shortcode"
109+
label={ __( 'Select block bindings', 'remote-data-blocks' ) }
110+
popoverProps={ { offset: 8, placement: 'bottom-start' } }
111+
text={ undefined }
138112
/>
139113
) }
140-
</Modal>
141-
) }
114+
</ToolbarGroup>
115+
</BlockControls>
116+
142117
{ showUI && fieldSelection && (
143118
<FieldShortcodeSelectFieldPopover
144119
contentRef={ contentRef }
145120
fieldSelection={ fieldSelection }
146121
formatTypeSettings={ formatTypeSettings }
147-
onClose={ onClose }
122+
onClose={ () => {
123+
setShowUI( false );
124+
onFocus();
125+
} }
148126
onSelectField={ ( data, fieldValue ) =>
149127
onSelectField( { ...data, selectionPath: 'popover' }, fieldValue )
150128
}
Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { __experimentalHeading as Heading } from '@wordpress/components';
1+
import { DropdownMenu, MenuGroup } from '@wordpress/components';
2+
import { __ } from '@wordpress/i18n';
3+
import { chevronRightSmall } from '@wordpress/icons';
24

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

17-
if ( remoteDatas.length === 0 ) {
18-
return (
19-
<div className="remote-data-blocks-select-existing">
20-
<p>No existing items.</p>
21-
</div>
22-
);
23-
}
24-
25-
return (
26-
<div className="remote-data-blocks-select-existing">
27-
{ remoteDatas.map( remoteData => (
28-
<div className="remote-data-blocks-existing-item" key={ remoteData.blockName }>
29-
<Heading className="remote-data-blocks-item-heading" level="4">
30-
{ blockConfigs[ remoteData.blockName ]?.settings.title ?? remoteData.blockName }
31-
</Heading>
32-
33-
<FieldSelectionFromAvailableBindings
34-
onSelectField={ ( data, fieldValue ) =>
35-
props.onSelectField( { ...data, selectionPath: 'select_existing_tab' }, fieldValue )
36-
}
37-
remoteData={ remoteData }
38-
/>
39-
</div>
40-
) ) }
41-
</div>
42-
);
19+
return remoteDatas.length > 0 ? (
20+
<DropdownMenu
21+
icon={ chevronRightSmall }
22+
label=""
23+
text={ __( 'Existing items', 'remote-data-blocks' ) }
24+
popoverProps={ {
25+
className: 'remote-data-blocks-field-shortcode-dropdown remote-data-blocks-select-existing',
26+
placement: 'right-start',
27+
offset: 0,
28+
} }
29+
>
30+
{ () =>
31+
remoteDatas.map( remoteData => (
32+
<MenuGroup
33+
key={ remoteData.blockName }
34+
label={ blockConfigs[ remoteData.blockName ]?.settings.title ?? remoteData.blockName }
35+
>
36+
<FieldSelectionFromAvailableBindings
37+
onSelectField={ ( data, fieldValue ) =>
38+
props.onSelectField( { ...data, selectionPath: 'select_existing_tab' }, fieldValue )
39+
}
40+
remoteData={ remoteData }
41+
/>
42+
</MenuGroup>
43+
) )
44+
}
45+
</DropdownMenu>
46+
) : undefined;
4347
}

src/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelectFieldPopover.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
__experimentalHeading as Heading,
88
Popover,
99
} from '@wordpress/components';
10+
import { __ } from '@wordpress/i18n';
1011
import { WPFormat, useAnchor } from '@wordpress/rich-text';
1112

1213
import { FieldShortcodeSelectField } from '@/blocks/remote-data-container/components/field-shortcode/FieldShortcodeSelection';
@@ -40,7 +41,7 @@ export function FieldShortcodeSelectFieldPopover( props: FieldShortcodeSelectFie
4041
>
4142
<Card style={ { width: '24rem' } }>
4243
<CardHeader>
43-
<Heading level={ 4 }>Select a field to bind</Heading>
44+
<Heading level={ 4 }>{ __( 'Select a field to bind', 'remote-data-blocks' ) }</Heading>
4445
</CardHeader>
4546
<CardBody>
4647
<FieldShortcodeSelectField
@@ -55,7 +56,7 @@ export function FieldShortcodeSelectFieldPopover( props: FieldShortcodeSelectFie
5556
</CardBody>
5657
<CardFooter>
5758
<Button onClick={ () => props.resetField( remoteData?.blockName ) } isDestructive>
58-
Reset field
59+
{ __( 'Reset field', 'remote-data-blocks' ) }
5960
</Button>
6061
</CardFooter>
6162
</Card>

0 commit comments

Comments
 (0)