@wordpress/block-editor 15.10.1-next.ba3aee3a2.0 → 15.10.1-next.v.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/components/block-bindings/attribute-control.cjs +1 -1
- package/build/components/block-bindings/attribute-control.cjs.map +1 -1
- package/build/components/block-bindings/source-fields-list.cjs +1 -1
- package/build/components/block-bindings/source-fields-list.cjs.map +1 -1
- package/build/components/block-tools/index.cjs +82 -70
- package/build/components/block-tools/index.cjs.map +2 -2
- package/build/components/block-visibility/block-visibility-info.cjs +0 -59
- package/build/components/block-visibility/block-visibility-info.cjs.map +3 -3
- package/build/components/block-visibility/constants.cjs +10 -5
- package/build/components/block-visibility/constants.cjs.map +2 -2
- package/build/components/block-visibility/index.cjs +13 -5
- package/build/components/block-visibility/index.cjs.map +3 -3
- package/build/components/block-visibility/modal.cjs +397 -0
- package/build/components/block-visibility/modal.cjs.map +7 -0
- package/build/components/block-visibility/toolbar.cjs +1 -1
- package/build/components/block-visibility/toolbar.cjs.map +2 -2
- package/build/components/block-visibility/use-block-visibility.cjs +13 -17
- package/build/components/block-visibility/use-block-visibility.cjs.map +2 -2
- package/build/components/block-visibility/utils.cjs +81 -0
- package/build/components/block-visibility/utils.cjs.map +7 -0
- package/build/components/block-visibility/viewport-menu-item.cjs +61 -0
- package/build/components/block-visibility/viewport-menu-item.cjs.map +7 -0
- package/build/components/block-visibility/viewport-toolbar.cjs +89 -0
- package/build/components/block-visibility/viewport-toolbar.cjs.map +7 -0
- package/build/components/inner-blocks/use-inner-block-template-sync.cjs +1 -1
- package/build/components/inner-blocks/use-inner-block-template-sync.cjs.map +1 -1
- package/build/components/inserter/menu.cjs +6 -2
- package/build/components/inserter/menu.cjs.map +2 -2
- package/build/components/list-view/block-select-button.cjs +2 -2
- package/build/components/list-view/block-select-button.cjs.map +2 -2
- package/build/components/list-view/block.cjs +39 -22
- package/build/components/list-view/block.cjs.map +2 -2
- package/build/components/rich-text/index.cjs +1 -1
- package/build/components/rich-text/index.cjs.map +1 -1
- package/build/components/url-input/index.cjs +2 -0
- package/build/components/url-input/index.cjs.map +2 -2
- package/build/components/use-block-commands/index.cjs +1 -1
- package/build/components/use-block-commands/index.cjs.map +2 -2
- package/build/hooks/block-fields/index.cjs +75 -166
- package/build/hooks/block-fields/index.cjs.map +2 -2
- package/build/hooks/block-fields/link/index.cjs +13 -23
- package/build/hooks/block-fields/link/index.cjs.map +2 -2
- package/build/hooks/block-fields/media/index.cjs +32 -58
- package/build/hooks/block-fields/media/index.cjs.map +2 -2
- package/build/hooks/block-fields/rich-text/index.cjs +1 -5
- package/build/hooks/block-fields/rich-text/index.cjs.map +2 -2
- package/build/hooks/cross-origin-isolation.cjs +102 -0
- package/build/hooks/cross-origin-isolation.cjs.map +7 -0
- package/build/hooks/index.cjs +1 -0
- package/build/hooks/index.cjs.map +2 -2
- package/build/layouts/flex.cjs +6 -2
- package/build/layouts/flex.cjs.map +2 -2
- package/build/store/private-selectors.cjs +33 -1
- package/build/store/private-selectors.cjs.map +3 -3
- package/build/store/reducer.cjs +1 -1
- package/build/store/reducer.cjs.map +1 -1
- package/build/store/selectors.cjs +7 -8
- package/build/store/selectors.cjs.map +2 -2
- package/build-module/components/block-bindings/attribute-control.mjs +1 -1
- package/build-module/components/block-bindings/attribute-control.mjs.map +1 -1
- package/build-module/components/block-bindings/source-fields-list.mjs +1 -1
- package/build-module/components/block-bindings/source-fields-list.mjs.map +1 -1
- package/build-module/components/block-tools/index.mjs +85 -73
- package/build-module/components/block-tools/index.mjs.map +2 -2
- package/build-module/components/block-visibility/block-visibility-info.mjs +0 -59
- package/build-module/components/block-visibility/block-visibility-info.mjs.map +3 -3
- package/build-module/components/block-visibility/constants.mjs +8 -4
- package/build-module/components/block-visibility/constants.mjs.map +2 -2
- package/build-module/components/block-visibility/index.mjs +13 -6
- package/build-module/components/block-visibility/index.mjs.map +2 -2
- package/build-module/components/block-visibility/modal.mjs +384 -0
- package/build-module/components/block-visibility/modal.mjs.map +7 -0
- package/build-module/components/block-visibility/toolbar.mjs +1 -1
- package/build-module/components/block-visibility/toolbar.mjs.map +2 -2
- package/build-module/components/block-visibility/use-block-visibility.mjs +13 -13
- package/build-module/components/block-visibility/use-block-visibility.mjs.map +2 -2
- package/build-module/components/block-visibility/utils.mjs +55 -0
- package/build-module/components/block-visibility/utils.mjs.map +7 -0
- package/build-module/components/block-visibility/viewport-menu-item.mjs +40 -0
- package/build-module/components/block-visibility/viewport-menu-item.mjs.map +7 -0
- package/build-module/components/block-visibility/viewport-toolbar.mjs +68 -0
- package/build-module/components/block-visibility/viewport-toolbar.mjs.map +7 -0
- package/build-module/components/inner-blocks/use-inner-block-template-sync.mjs +1 -1
- package/build-module/components/inner-blocks/use-inner-block-template-sync.mjs.map +1 -1
- package/build-module/components/inserter/menu.mjs +6 -2
- package/build-module/components/inserter/menu.mjs.map +2 -2
- package/build-module/components/list-view/block-select-button.mjs +2 -2
- package/build-module/components/list-view/block-select-button.mjs.map +2 -2
- package/build-module/components/list-view/block.mjs +39 -22
- package/build-module/components/list-view/block.mjs.map +2 -2
- package/build-module/components/rich-text/index.mjs +1 -1
- package/build-module/components/rich-text/index.mjs.map +1 -1
- package/build-module/components/url-input/index.mjs +2 -0
- package/build-module/components/url-input/index.mjs.map +2 -2
- package/build-module/components/use-block-commands/index.mjs +1 -1
- package/build-module/components/use-block-commands/index.mjs.map +2 -2
- package/build-module/hooks/block-fields/index.mjs +75 -166
- package/build-module/hooks/block-fields/index.mjs.map +2 -2
- package/build-module/hooks/block-fields/link/index.mjs +13 -23
- package/build-module/hooks/block-fields/link/index.mjs.map +2 -2
- package/build-module/hooks/block-fields/media/index.mjs +32 -58
- package/build-module/hooks/block-fields/media/index.mjs.map +2 -2
- package/build-module/hooks/block-fields/rich-text/index.mjs +1 -5
- package/build-module/hooks/block-fields/rich-text/index.mjs.map +2 -2
- package/build-module/hooks/cross-origin-isolation.mjs +100 -0
- package/build-module/hooks/cross-origin-isolation.mjs.map +7 -0
- package/build-module/hooks/index.mjs +1 -0
- package/build-module/hooks/index.mjs.map +2 -2
- package/build-module/layouts/flex.mjs +6 -2
- package/build-module/layouts/flex.mjs.map +2 -2
- package/build-module/store/private-selectors.mjs +34 -1
- package/build-module/store/private-selectors.mjs.map +2 -2
- package/build-module/store/reducer.mjs +1 -1
- package/build-module/store/reducer.mjs.map +1 -1
- package/build-module/store/selectors.mjs +7 -8
- package/build-module/store/selectors.mjs.map +2 -2
- package/build-style/content-rtl.css +4 -1
- package/build-style/content.css +4 -1
- package/build-style/style-rtl.css +48 -0
- package/build-style/style.css +48 -0
- package/package.json +39 -39
- package/src/components/block-bindings/attribute-control.js +1 -1
- package/src/components/block-bindings/source-fields-list.js +1 -1
- package/src/components/block-list/content.scss +4 -1
- package/src/components/block-tools/index.js +45 -33
- package/src/components/block-visibility/block-visibility-info.js +0 -1
- package/src/components/block-visibility/constants.js +7 -3
- package/src/components/block-visibility/index.js +21 -3
- package/src/components/block-visibility/modal.js +358 -0
- package/src/components/block-visibility/style.scss +58 -0
- package/src/components/block-visibility/test/use-block-visibility.js +12 -56
- package/src/components/block-visibility/test/utils.js +266 -0
- package/src/components/block-visibility/toolbar.js +1 -1
- package/src/components/block-visibility/use-block-visibility.js +18 -21
- package/src/components/block-visibility/utils.js +95 -0
- package/src/components/block-visibility/viewport-menu-item.js +42 -0
- package/src/components/block-visibility/viewport-toolbar.js +88 -0
- package/src/components/inner-blocks/use-inner-block-template-sync.js +1 -1
- package/src/components/inserter/menu.js +6 -2
- package/src/components/list-view/block-select-button.js +2 -2
- package/src/components/list-view/block.js +47 -25
- package/src/components/rich-text/index.js +1 -1
- package/src/components/url-input/index.js +2 -0
- package/src/components/use-block-commands/index.js +4 -3
- package/src/hooks/block-fields/index.js +104 -224
- package/src/hooks/block-fields/link/index.js +13 -39
- package/src/hooks/block-fields/media/index.js +31 -90
- package/src/hooks/block-fields/rich-text/index.js +1 -5
- package/src/hooks/block-fields/styles.scss +2 -0
- package/src/hooks/cross-origin-isolation.js +143 -0
- package/src/hooks/index.js +1 -0
- package/src/layouts/flex.js +8 -3
- package/src/layouts/test/flex.js +53 -0
- package/src/store/private-selectors.js +64 -1
- package/src/store/reducer.js +1 -1
- package/src/store/selectors.js +7 -9
- package/src/store/test/private-selectors.js +80 -0
- package/src/style.scss +1 -0
- package/src/components/block-visibility/styles.scss +0 -10
|
@@ -54,6 +54,7 @@ import AriaReferencedText from './aria-referenced-text';
|
|
|
54
54
|
import { unlock } from '../../lock-unlock';
|
|
55
55
|
import usePasteStyles from '../use-paste-styles';
|
|
56
56
|
import { cleanEmptyObject } from '../../hooks/utils';
|
|
57
|
+
import { BlockVisibilityModal } from '../block-visibility';
|
|
57
58
|
|
|
58
59
|
function ListViewBlock( {
|
|
59
60
|
block: { clientId },
|
|
@@ -79,7 +80,8 @@ function ListViewBlock( {
|
|
|
79
80
|
const settingsRef = useRef( null );
|
|
80
81
|
const [ isHovered, setIsHovered ] = useState( false );
|
|
81
82
|
const [ settingsAnchorRect, setSettingsAnchorRect ] = useState();
|
|
82
|
-
|
|
83
|
+
const [ visibilityModalClientIds, setVisibilityModalClientIds ] =
|
|
84
|
+
useState( null );
|
|
83
85
|
const { isLocked } = useBlockLock( clientId );
|
|
84
86
|
|
|
85
87
|
const isFirstSelectedBlock =
|
|
@@ -98,6 +100,7 @@ function ListViewBlock( {
|
|
|
98
100
|
insertBeforeBlock,
|
|
99
101
|
updateBlockAttributes,
|
|
100
102
|
} = unlock( useDispatch( blockEditorStore ) );
|
|
103
|
+
|
|
101
104
|
const debouncedToggleBlockHighlight = useDebounce(
|
|
102
105
|
toggleBlockHighlight,
|
|
103
106
|
50
|
|
@@ -125,15 +128,18 @@ function ListViewBlock( {
|
|
|
125
128
|
const { block, blockName, allowRightClickOverrides, isBlockHidden } =
|
|
126
129
|
useSelect(
|
|
127
130
|
( select ) => {
|
|
128
|
-
const {
|
|
129
|
-
|
|
131
|
+
const {
|
|
132
|
+
getBlock,
|
|
133
|
+
getBlockName: _getBlockName,
|
|
134
|
+
getSettings,
|
|
135
|
+
} = select( blockEditorStore );
|
|
130
136
|
const { isBlockHidden: _isBlockHidden } = unlock(
|
|
131
137
|
select( blockEditorStore )
|
|
132
138
|
);
|
|
133
139
|
|
|
134
140
|
return {
|
|
135
141
|
block: getBlock( clientId ),
|
|
136
|
-
blockName:
|
|
142
|
+
blockName: _getBlockName( clientId ),
|
|
137
143
|
allowRightClickOverrides:
|
|
138
144
|
getSettings().allowRightClickOverrides,
|
|
139
145
|
isBlockHidden: _isBlockHidden( clientId ),
|
|
@@ -373,30 +379,40 @@ function ListViewBlock( {
|
|
|
373
379
|
event.preventDefault();
|
|
374
380
|
const { blocksToUpdate } = getBlocksToUpdate();
|
|
375
381
|
const blocks = getBlocksByClientId( blocksToUpdate );
|
|
376
|
-
const
|
|
377
|
-
hasBlockSupport(
|
|
382
|
+
const supportsBlockVisibility = blocks.every( ( _block ) =>
|
|
383
|
+
hasBlockSupport( _block.name, 'visibility', true )
|
|
378
384
|
);
|
|
379
|
-
|
|
385
|
+
|
|
386
|
+
if ( ! supportsBlockVisibility ) {
|
|
380
387
|
return;
|
|
381
388
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
blocks.
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
389
|
+
|
|
390
|
+
if ( window.__experimentalHideBlocksBasedOnScreenSize ) {
|
|
391
|
+
// Open the visibility breakpoints modal.
|
|
392
|
+
setVisibilityModalClientIds( blocksToUpdate );
|
|
393
|
+
} else {
|
|
394
|
+
const hasHiddenBlock = blocks.some(
|
|
395
|
+
( blockToUpdate ) =>
|
|
396
|
+
blockToUpdate.attributes.metadata?.blockVisibility ===
|
|
397
|
+
false
|
|
398
|
+
);
|
|
399
|
+
const attributesByClientId = Object.fromEntries(
|
|
400
|
+
blocks.map( ( { clientId: mapClientId, attributes } ) => [
|
|
401
|
+
mapClientId,
|
|
402
|
+
{
|
|
403
|
+
metadata: cleanEmptyObject( {
|
|
404
|
+
...attributes?.metadata,
|
|
405
|
+
blockVisibility: hasHiddenBlock
|
|
406
|
+
? undefined
|
|
407
|
+
: false,
|
|
408
|
+
} ),
|
|
409
|
+
},
|
|
410
|
+
] )
|
|
411
|
+
);
|
|
412
|
+
updateBlockAttributes( blocksToUpdate, attributesByClientId, {
|
|
413
|
+
uniqueByBlock: true,
|
|
414
|
+
} );
|
|
415
|
+
}
|
|
400
416
|
}
|
|
401
417
|
}
|
|
402
418
|
|
|
@@ -699,6 +715,12 @@ function ListViewBlock( {
|
|
|
699
715
|
) }
|
|
700
716
|
</TreeGridCell>
|
|
701
717
|
) }
|
|
718
|
+
{ visibilityModalClientIds && (
|
|
719
|
+
<BlockVisibilityModal
|
|
720
|
+
clientIds={ visibilityModalClientIds }
|
|
721
|
+
onClose={ () => setVisibilityModalClientIds( null ) }
|
|
722
|
+
/>
|
|
723
|
+
) }
|
|
702
724
|
</ListViewLeaf>
|
|
703
725
|
);
|
|
704
726
|
}
|
|
@@ -452,6 +452,8 @@ class URLInput extends Component {
|
|
|
452
452
|
value,
|
|
453
453
|
required: true,
|
|
454
454
|
type: 'text',
|
|
455
|
+
name: inputId,
|
|
456
|
+
autoComplete: 'off',
|
|
455
457
|
onChange: disabled ? () => {} : this.onChange, // Disable onChange when disabled
|
|
456
458
|
onFocus: disabled ? () => {} : this.onFocus, // Disable onFocus when disabled
|
|
457
459
|
placeholder,
|
|
@@ -223,9 +223,10 @@ const getQuickActionsCommands = () =>
|
|
|
223
223
|
} );
|
|
224
224
|
const canRemove = canRemoveBlocks( clientIds );
|
|
225
225
|
|
|
226
|
-
const canToggleBlockVisibility =
|
|
227
|
-
|
|
228
|
-
|
|
226
|
+
const canToggleBlockVisibility =
|
|
227
|
+
blocks.every( ( { clientId } ) =>
|
|
228
|
+
hasBlockSupport( getBlockName( clientId ), 'visibility', true )
|
|
229
|
+
) && ! window.__experimentalHideBlocksBasedOnScreenSize;
|
|
229
230
|
|
|
230
231
|
const commands = [];
|
|
231
232
|
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { createHigherOrderComponent } from '@wordpress/compose';
|
|
11
11
|
import { DataForm } from '@wordpress/dataviews';
|
|
12
12
|
import { useContext, useState, useMemo } from '@wordpress/element';
|
|
13
|
+
import { __ } from '@wordpress/i18n';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Internal dependencies
|
|
@@ -38,138 +39,41 @@ const CONTROLS = {
|
|
|
38
39
|
* Creates a configured control component that wraps a custom control
|
|
39
40
|
* and passes configuration as props.
|
|
40
41
|
*
|
|
41
|
-
* @param {
|
|
42
|
-
* @param {string}
|
|
42
|
+
* @param {Component} ControlComponent The React component for the control.
|
|
43
|
+
* @param {string} type The type of control.
|
|
44
|
+
* @param {Object} config The control configuration passed as a prop.
|
|
45
|
+
*
|
|
43
46
|
* @return {Function} A wrapped control component
|
|
44
47
|
*/
|
|
45
|
-
function createConfiguredControl( config ) {
|
|
46
|
-
const { control, ...controlConfig } = config;
|
|
47
|
-
const ControlComponent = CONTROLS[ control ];
|
|
48
|
-
|
|
48
|
+
function createConfiguredControl( ControlComponent, type, config ) {
|
|
49
49
|
if ( ! ControlComponent ) {
|
|
50
|
-
throw new Error( `Control type "${
|
|
50
|
+
throw new Error( `Control type "${ type }" not found` );
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
return function ConfiguredControl( props ) {
|
|
54
|
-
return <ControlComponent { ...props } config={
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Normalize a media value to a canonical structure.
|
|
60
|
-
* Only includes properties that are present in the field's mapping (if provided).
|
|
61
|
-
*
|
|
62
|
-
* @param {Object} value - The mapped value from the block attributes (with canonical keys)
|
|
63
|
-
* @param {Object} fieldDef - Optional field definition containing the mapping
|
|
64
|
-
* @return {Object} Normalized media value with canonical properties
|
|
65
|
-
*/
|
|
66
|
-
function normalizeMediaValue( value, fieldDef ) {
|
|
67
|
-
const defaults = {
|
|
68
|
-
id: null,
|
|
69
|
-
url: '',
|
|
70
|
-
caption: '',
|
|
71
|
-
alt: '',
|
|
72
|
-
type: 'image',
|
|
73
|
-
poster: '',
|
|
74
|
-
featuredImage: false,
|
|
75
|
-
link: '',
|
|
54
|
+
return <ControlComponent { ...props } config={ config } />;
|
|
76
55
|
};
|
|
77
|
-
|
|
78
|
-
const result = {};
|
|
79
|
-
|
|
80
|
-
// If there's a mapping, only include properties that are in it
|
|
81
|
-
if ( fieldDef?.mapping ) {
|
|
82
|
-
Object.keys( fieldDef.mapping ).forEach( ( key ) => {
|
|
83
|
-
result[ key ] = value?.[ key ] ?? defaults[ key ] ?? '';
|
|
84
|
-
} );
|
|
85
|
-
return result;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Without mapping, include all default properties
|
|
89
|
-
Object.keys( defaults ).forEach( ( key ) => {
|
|
90
|
-
result[ key ] = value?.[ key ] ?? defaults[ key ];
|
|
91
|
-
} );
|
|
92
|
-
return result;
|
|
93
56
|
}
|
|
94
57
|
|
|
95
58
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* @param {Object}
|
|
100
|
-
* @param {Object}
|
|
101
|
-
* @
|
|
59
|
+
* Component that renders a DataForm for a single block's attributes
|
|
60
|
+
* @param {Object} props
|
|
61
|
+
* @param {string} props.clientId The clientId of the block.
|
|
62
|
+
* @param {Object} props.blockType The blockType definition.
|
|
63
|
+
* @param {Object} props.attributes The block's attribute values.
|
|
64
|
+
* @param {Function} props.setAttributes Action to set the block's attributes.
|
|
65
|
+
* @param {boolean} props.isCollapsed Whether the DataForm is rendered as 'collapsed' with only the first field
|
|
66
|
+
* displayed by default. When collapsed a dropdown is displayed to allow
|
|
67
|
+
* displaying additional fields. The block's title is displayed as the title.
|
|
68
|
+
* The collapsed mode is often used when multiple BlockForms are shown together.
|
|
102
69
|
*/
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if ( key in value ) {
|
|
111
|
-
result[ key ] = value[ key ];
|
|
112
|
-
}
|
|
113
|
-
} );
|
|
114
|
-
return result;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Normalize a link value to a canonical structure.
|
|
119
|
-
* Only includes properties that are present in the field's mapping (if provided).
|
|
120
|
-
*
|
|
121
|
-
* @param {Object} value - The mapped value from the block attributes (with canonical keys)
|
|
122
|
-
* @param {Object} fieldDef - Optional field definition containing the mapping
|
|
123
|
-
* @return {Object} Normalized link value with canonical properties
|
|
124
|
-
*/
|
|
125
|
-
function normalizeLinkValue( value, fieldDef ) {
|
|
126
|
-
const defaults = {
|
|
127
|
-
url: '',
|
|
128
|
-
rel: '',
|
|
129
|
-
linkTarget: '',
|
|
130
|
-
destination: '',
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const result = {};
|
|
134
|
-
|
|
135
|
-
// If there's a mapping, only include properties that are in it
|
|
136
|
-
if ( fieldDef?.mapping ) {
|
|
137
|
-
Object.keys( fieldDef.mapping ).forEach( ( key ) => {
|
|
138
|
-
result[ key ] = value?.[ key ] ?? defaults[ key ] ?? '';
|
|
139
|
-
} );
|
|
140
|
-
return result;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Without mapping, include all default properties
|
|
144
|
-
Object.keys( defaults ).forEach( ( key ) => {
|
|
145
|
-
result[ key ] = value?.[ key ] ?? defaults[ key ];
|
|
146
|
-
} );
|
|
147
|
-
return result;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Denormalize a link value from canonical structure back to mapped keys.
|
|
152
|
-
* Only includes properties that are present in the field's mapping.
|
|
153
|
-
*
|
|
154
|
-
* @param {Object} value - The normalized link value
|
|
155
|
-
* @param {Object} fieldDef - The field definition containing the mapping
|
|
156
|
-
* @return {Object} Value with only mapped properties
|
|
157
|
-
*/
|
|
158
|
-
function denormalizeLinkValue( value, fieldDef ) {
|
|
159
|
-
if ( ! fieldDef.mapping ) {
|
|
160
|
-
return value;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const result = {};
|
|
164
|
-
Object.entries( fieldDef.mapping ).forEach( ( [ key ] ) => {
|
|
165
|
-
if ( key in value ) {
|
|
166
|
-
result[ key ] = value[ key ];
|
|
167
|
-
}
|
|
168
|
-
} );
|
|
169
|
-
return result;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function BlockFields( { clientId, blockType, attributes, setAttributes } ) {
|
|
70
|
+
function BlockFields( {
|
|
71
|
+
clientId,
|
|
72
|
+
blockType,
|
|
73
|
+
attributes,
|
|
74
|
+
setAttributes,
|
|
75
|
+
isCollapsed = false,
|
|
76
|
+
} ) {
|
|
173
77
|
const blockTitle = useBlockDisplayTitle( {
|
|
174
78
|
clientId,
|
|
175
79
|
context: 'list-view',
|
|
@@ -178,9 +82,19 @@ function BlockFields( { clientId, blockType, attributes, setAttributes } ) {
|
|
|
178
82
|
|
|
179
83
|
const blockTypeFields = blockType?.[ fieldsKey ];
|
|
180
84
|
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
85
|
+
const computedForm = useMemo( () => {
|
|
86
|
+
if ( ! isCollapsed ) {
|
|
87
|
+
return blockType?.[ formKey ];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// For a collapsed form only show the first field by default.
|
|
91
|
+
return {
|
|
92
|
+
...blockType?.[ formKey ],
|
|
93
|
+
fields: [ blockType?.[ formKey ]?.fields?.[ 0 ] ],
|
|
94
|
+
};
|
|
95
|
+
}, [ blockType, isCollapsed ] );
|
|
96
|
+
|
|
97
|
+
const [ form, setForm ] = useState( computedForm );
|
|
184
98
|
|
|
185
99
|
// Build DataForm fields with proper structure
|
|
186
100
|
const dataFormFields = useMemo( () => {
|
|
@@ -189,100 +103,63 @@ function BlockFields( { clientId, blockType, attributes, setAttributes } ) {
|
|
|
189
103
|
}
|
|
190
104
|
|
|
191
105
|
return blockTypeFields.map( ( fieldDef ) => {
|
|
192
|
-
const ControlComponent = CONTROLS[ fieldDef.type ];
|
|
193
|
-
|
|
194
|
-
const defaultValues = {};
|
|
195
|
-
if ( fieldDef.mapping && blockType?.attributes ) {
|
|
196
|
-
Object.entries( fieldDef.mapping ).forEach(
|
|
197
|
-
( [ key, attrKey ] ) => {
|
|
198
|
-
defaultValues[ key ] =
|
|
199
|
-
blockType.attributes[ attrKey ]?.defaultValue ??
|
|
200
|
-
undefined;
|
|
201
|
-
}
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
106
|
const field = {
|
|
206
107
|
id: fieldDef.id,
|
|
207
108
|
label: fieldDef.label,
|
|
208
109
|
type: fieldDef.type, // Use the field's type; DataForm will use built-in or custom Edit
|
|
209
|
-
|
|
210
|
-
hideLabelFromVision: fieldDef.id === 'content',
|
|
211
|
-
// getValue and setValue handle the mapping to block attributes
|
|
212
|
-
getValue: ( { item } ) => {
|
|
213
|
-
if ( fieldDef.mapping ) {
|
|
214
|
-
// Extract mapped properties from the block attributes
|
|
215
|
-
const mappedValue = {};
|
|
216
|
-
Object.entries( fieldDef.mapping ).forEach(
|
|
217
|
-
( [ key, attrKey ] ) => {
|
|
218
|
-
mappedValue[ key ] = item[ attrKey ];
|
|
219
|
-
}
|
|
220
|
-
);
|
|
110
|
+
};
|
|
221
111
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
112
|
+
// If the field defines a `mapping`, then custom `getValue` and `setValue`
|
|
113
|
+
// implementations are provided.
|
|
114
|
+
// These functions map from the inconsistent attribute keys found on blocks
|
|
115
|
+
// to consistent keys that the field can use internally (and back again).
|
|
116
|
+
// When `mapping` isn't provided, we can use the field API's default
|
|
117
|
+
// implementation of these functions.
|
|
118
|
+
if ( fieldDef.mapping ) {
|
|
119
|
+
field.getValue = ( { item } ) => {
|
|
120
|
+
// Extract mapped properties from the block attributes
|
|
121
|
+
const mappedValue = {};
|
|
122
|
+
Object.entries( fieldDef.mapping ).forEach(
|
|
123
|
+
( [ key, attrKey ] ) => {
|
|
124
|
+
mappedValue[ key ] = item[ attrKey ];
|
|
228
125
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if ( fieldDef.mapping ) {
|
|
238
|
-
// Denormalize from canonical structure back to mapped keys
|
|
239
|
-
let denormalizedValue = value;
|
|
240
|
-
if ( fieldDef.type === 'media' ) {
|
|
241
|
-
denormalizedValue = denormalizeMediaValue(
|
|
242
|
-
value,
|
|
243
|
-
fieldDef
|
|
244
|
-
);
|
|
245
|
-
} else if ( fieldDef.type === 'link' ) {
|
|
246
|
-
denormalizedValue = denormalizeLinkValue(
|
|
247
|
-
value,
|
|
248
|
-
fieldDef
|
|
249
|
-
);
|
|
126
|
+
);
|
|
127
|
+
return mappedValue;
|
|
128
|
+
};
|
|
129
|
+
field.setValue = ( { value } ) => {
|
|
130
|
+
const attributeUpdates = {};
|
|
131
|
+
Object.entries( fieldDef.mapping ).forEach(
|
|
132
|
+
( [ key, attrKey ] ) => {
|
|
133
|
+
attributeUpdates[ attrKey ] = value[ key ];
|
|
250
134
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
( [ key, attrKey ] ) => {
|
|
256
|
-
// If key is explicitly in value, use it (even if undefined to allow clearing)
|
|
257
|
-
// Otherwise, preserve the old value
|
|
258
|
-
if ( key in denormalizedValue ) {
|
|
259
|
-
updates[ attrKey ] =
|
|
260
|
-
denormalizedValue[ key ];
|
|
261
|
-
} else {
|
|
262
|
-
updates[ attrKey ] = item[ attrKey ];
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
);
|
|
266
|
-
return updates;
|
|
267
|
-
}
|
|
268
|
-
// For simple id-based fields, use the id as the attribute key
|
|
269
|
-
return { [ fieldDef.id ]: value };
|
|
270
|
-
},
|
|
271
|
-
};
|
|
135
|
+
);
|
|
136
|
+
return attributeUpdates;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
272
139
|
|
|
273
140
|
// Only add custom Edit component if one exists for this type
|
|
141
|
+
const ControlComponent = CONTROLS[ fieldDef.type ];
|
|
274
142
|
if ( ControlComponent ) {
|
|
275
143
|
// Use EditConfig pattern: Edit is an object with control type and config props
|
|
276
|
-
field.Edit = createConfiguredControl(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
144
|
+
field.Edit = createConfiguredControl(
|
|
145
|
+
ControlComponent,
|
|
146
|
+
fieldDef.type,
|
|
147
|
+
{
|
|
148
|
+
clientId,
|
|
149
|
+
fieldDef,
|
|
150
|
+
}
|
|
151
|
+
);
|
|
281
152
|
}
|
|
282
153
|
|
|
283
154
|
return field;
|
|
284
155
|
} );
|
|
285
|
-
}, [ blockTypeFields,
|
|
156
|
+
}, [ blockTypeFields, clientId ] );
|
|
157
|
+
|
|
158
|
+
if ( ! blockTypeFields?.length ) {
|
|
159
|
+
// TODO - we might still want to show a placeholder for blocks with no fields.
|
|
160
|
+
// for example, a way to select the block.
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
286
163
|
|
|
287
164
|
const handleToggleField = ( fieldId ) => {
|
|
288
165
|
setForm( ( prev ) => {
|
|
@@ -300,31 +177,33 @@ function BlockFields( { clientId, blockType, attributes, setAttributes } ) {
|
|
|
300
177
|
} );
|
|
301
178
|
};
|
|
302
179
|
|
|
303
|
-
if ( ! blockTypeFields?.length ) {
|
|
304
|
-
// TODO - we might still want to show a placeholder for blocks with no fields.
|
|
305
|
-
// for example, a way to select the block.
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
180
|
return (
|
|
310
181
|
<div className="block-editor-block-fields__container">
|
|
311
182
|
<div className="block-editor-block-fields__header">
|
|
312
183
|
<HStack spacing={ 1 }>
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
184
|
+
{ isCollapsed && (
|
|
185
|
+
<>
|
|
186
|
+
<BlockIcon
|
|
187
|
+
className="block-editor-block-fields__header-icon"
|
|
188
|
+
icon={ blockInformation?.icon }
|
|
189
|
+
/>
|
|
190
|
+
<h2 className="block-editor-block-fields__header-title">
|
|
191
|
+
<Truncate numberOfLines={ 1 }>
|
|
192
|
+
{ blockTitle }
|
|
193
|
+
</Truncate>
|
|
194
|
+
</h2>
|
|
195
|
+
<FieldsDropdownMenu
|
|
196
|
+
fields={ dataFormFields }
|
|
197
|
+
visibleFields={ form.fields }
|
|
198
|
+
onToggleField={ handleToggleField }
|
|
199
|
+
/>
|
|
200
|
+
</>
|
|
201
|
+
) }
|
|
202
|
+
{ ! isCollapsed && (
|
|
203
|
+
<h2 className="block-editor-block-fields__header-title">
|
|
204
|
+
{ __( 'Content' ) }
|
|
205
|
+
</h2>
|
|
206
|
+
) }
|
|
328
207
|
</HStack>
|
|
329
208
|
</div>
|
|
330
209
|
<DataForm
|
|
@@ -370,6 +249,7 @@ const withBlockFields = createHigherOrderComponent(
|
|
|
370
249
|
<BlockFields
|
|
371
250
|
{ ...props }
|
|
372
251
|
blockType={ blockType }
|
|
252
|
+
isCollapsed
|
|
373
253
|
/>
|
|
374
254
|
</PrivateInspectorControlsFill>
|
|
375
255
|
)
|
|
@@ -73,11 +73,6 @@ export default function Link( { data, field, onChange, config = {} } ) {
|
|
|
73
73
|
isControl: true,
|
|
74
74
|
} );
|
|
75
75
|
const { fieldDef } = config;
|
|
76
|
-
const updateAttributes = ( newValue ) => {
|
|
77
|
-
const mappedChanges = field.setValue( { item: data, value: newValue } );
|
|
78
|
-
onChange( mappedChanges );
|
|
79
|
-
};
|
|
80
|
-
|
|
81
76
|
const value = field.getValue( { item: data } );
|
|
82
77
|
const url = value?.url;
|
|
83
78
|
const rel = value?.rel || '';
|
|
@@ -145,30 +140,12 @@ export default function Link( { data, field, onChange, config = {} } ) {
|
|
|
145
140
|
...newValues,
|
|
146
141
|
} );
|
|
147
142
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if ( key === 'href' || key === 'url' ) {
|
|
155
|
-
updateValue[ key ] =
|
|
156
|
-
updatedAttrs.url;
|
|
157
|
-
} else if ( key === 'rel' ) {
|
|
158
|
-
updateValue[ key ] =
|
|
159
|
-
updatedAttrs.rel;
|
|
160
|
-
} else if (
|
|
161
|
-
key === 'target' ||
|
|
162
|
-
key === 'linkTarget'
|
|
163
|
-
) {
|
|
164
|
-
updateValue[ key ] =
|
|
165
|
-
updatedAttrs.linkTarget;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
updateAttributes( updateValue );
|
|
143
|
+
onChange(
|
|
144
|
+
field.setValue( {
|
|
145
|
+
item: data,
|
|
146
|
+
value: updatedAttrs,
|
|
147
|
+
} )
|
|
148
|
+
);
|
|
172
149
|
} }
|
|
173
150
|
onRemove={ () => {
|
|
174
151
|
// Remove all link-related properties based on what's in the mapping
|
|
@@ -177,20 +154,17 @@ export default function Link( { data, field, onChange, config = {} } ) {
|
|
|
177
154
|
if ( fieldDef?.mapping ) {
|
|
178
155
|
Object.keys( fieldDef.mapping ).forEach(
|
|
179
156
|
( key ) => {
|
|
180
|
-
|
|
181
|
-
key === 'href' ||
|
|
182
|
-
key === 'url' ||
|
|
183
|
-
key === 'rel' ||
|
|
184
|
-
key === 'target' ||
|
|
185
|
-
key === 'linkTarget'
|
|
186
|
-
) {
|
|
187
|
-
removeValue[ key ] = undefined;
|
|
188
|
-
}
|
|
157
|
+
removeValue[ key ] = undefined;
|
|
189
158
|
}
|
|
190
159
|
);
|
|
191
160
|
}
|
|
192
161
|
|
|
193
|
-
|
|
162
|
+
onChange(
|
|
163
|
+
field.setValue( {
|
|
164
|
+
item: data,
|
|
165
|
+
value: removeValue,
|
|
166
|
+
} )
|
|
167
|
+
);
|
|
194
168
|
} }
|
|
195
169
|
/>
|
|
196
170
|
</Popover>
|