@wordpress/block-library 9.37.2-next.ba3aee3a2.0 → 9.38.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/CHANGELOG.md +1 -1
- package/build/audio/index.cjs +16 -8
- package/build/audio/index.cjs.map +2 -2
- package/build/block-keyboard-shortcuts/index.cjs +2 -4
- package/build/block-keyboard-shortcuts/index.cjs.map +2 -2
- package/build/button/index.cjs +17 -8
- package/build/button/index.cjs.map +2 -2
- package/build/code/index.cjs +3 -1
- package/build/code/index.cjs.map +2 -2
- package/build/comments-title/edit.cjs +10 -6
- package/build/comments-title/edit.cjs.map +2 -2
- package/build/cover/index.cjs +20 -10
- package/build/cover/index.cjs.map +2 -2
- package/build/details/index.cjs +3 -1
- package/build/details/index.cjs.map +2 -2
- package/build/file/index.cjs +19 -9
- package/build/file/index.cjs.map +2 -2
- package/build/heading/block.json +1 -3
- package/build/heading/deprecated.cjs +101 -5
- package/build/heading/deprecated.cjs.map +3 -3
- package/build/heading/edit.cjs +20 -41
- package/build/heading/edit.cjs.map +3 -3
- package/build/heading/index.cjs +3 -1
- package/build/heading/index.cjs.map +2 -2
- package/build/heading/save.cjs +2 -16
- package/build/heading/save.cjs.map +3 -3
- package/build/heading/transforms.cjs +16 -3
- package/build/heading/transforms.cjs.map +2 -2
- package/build/image/image.cjs +3 -3
- package/build/image/image.cjs.map +2 -2
- package/build/image/index.cjs +33 -17
- package/build/image/index.cjs.map +2 -2
- package/build/list-item/index.cjs +3 -1
- package/build/list-item/index.cjs.map +2 -2
- package/build/media-text/index.cjs +30 -15
- package/build/media-text/index.cjs.map +2 -2
- package/build/more/block.json +1 -0
- package/build/more/index.cjs +3 -1
- package/build/more/index.cjs.map +2 -2
- package/build/navigation/edit/index.cjs +7 -6
- package/build/navigation/edit/index.cjs.map +2 -2
- package/build/navigation/edit/responsive-wrapper.cjs +7 -4
- package/build/navigation/edit/responsive-wrapper.cjs.map +2 -2
- package/build/navigation/edit/use-create-overlay.cjs +2 -0
- package/build/navigation/edit/use-create-overlay.cjs.map +2 -2
- package/build/navigation-link/edit.cjs +8 -58
- package/build/navigation-link/edit.cjs.map +3 -3
- package/build/navigation-link/index.cjs +13 -8
- package/build/navigation-link/index.cjs.map +2 -2
- package/build/navigation-link/shared/index.cjs +11 -2
- package/build/navigation-link/shared/index.cjs.map +2 -2
- package/build/navigation-link/shared/invalid-draft-display.cjs +82 -0
- package/build/navigation-link/shared/invalid-draft-display.cjs.map +7 -0
- package/build/navigation-link/shared/use-enable-link-status-validation.cjs +50 -0
- package/build/navigation-link/shared/use-enable-link-status-validation.cjs.map +7 -0
- package/build/navigation-link/shared/use-is-invalid-link.cjs +64 -0
- package/build/navigation-link/shared/use-is-invalid-link.cjs.map +7 -0
- package/build/navigation-overlay-close/block.json +1 -4
- package/build/navigation-overlay-close/index.cjs +3 -25
- package/build/navigation-overlay-close/index.cjs.map +3 -3
- package/build/navigation-submenu/edit.cjs +36 -18
- package/build/navigation-submenu/edit.cjs.map +2 -2
- package/build/navigation-submenu/index.cjs +15 -8
- package/build/navigation-submenu/index.cjs.map +2 -2
- package/build/nextpage/block.json +1 -0
- package/build/paragraph/index.cjs +3 -1
- package/build/paragraph/index.cjs.map +2 -2
- package/build/paragraph/transforms.cjs +14 -3
- package/build/paragraph/transforms.cjs.map +3 -3
- package/build/preformatted/index.cjs +3 -1
- package/build/preformatted/index.cjs.map +2 -2
- package/build/pullquote/index.cjs +7 -3
- package/build/pullquote/index.cjs.map +2 -2
- package/build/search/index.cjs +10 -5
- package/build/search/index.cjs.map +2 -2
- package/build/social-link/index.cjs +13 -7
- package/build/social-link/index.cjs.map +2 -2
- package/build/table-of-contents/hooks.cjs +1 -1
- package/build/table-of-contents/hooks.cjs.map +1 -1
- package/build/template-part/edit/utils/get-template-part-icon.cjs.map +1 -1
- package/build/utils/is-within-overlay.cjs +52 -0
- package/build/utils/is-within-overlay.cjs.map +7 -0
- package/build/verse/index.cjs +3 -1
- package/build/verse/index.cjs.map +2 -2
- package/build/video/index.cjs +20 -10
- package/build/video/index.cjs.map +2 -2
- package/build-module/audio/index.mjs +16 -8
- package/build-module/audio/index.mjs.map +2 -2
- package/build-module/block-keyboard-shortcuts/index.mjs +2 -4
- package/build-module/block-keyboard-shortcuts/index.mjs.map +2 -2
- package/build-module/button/index.mjs +17 -8
- package/build-module/button/index.mjs.map +2 -2
- package/build-module/code/index.mjs +3 -1
- package/build-module/code/index.mjs.map +2 -2
- package/build-module/comments-title/edit.mjs +10 -6
- package/build-module/comments-title/edit.mjs.map +2 -2
- package/build-module/cover/index.mjs +20 -10
- package/build-module/cover/index.mjs.map +2 -2
- package/build-module/details/index.mjs +3 -1
- package/build-module/details/index.mjs.map +2 -2
- package/build-module/file/index.mjs +19 -9
- package/build-module/file/index.mjs.map +2 -2
- package/build-module/heading/block.json +1 -3
- package/build-module/heading/deprecated.mjs +101 -5
- package/build-module/heading/deprecated.mjs.map +2 -2
- package/build-module/heading/edit.mjs +22 -46
- package/build-module/heading/edit.mjs.map +2 -2
- package/build-module/heading/index.mjs +3 -1
- package/build-module/heading/index.mjs.map +2 -2
- package/build-module/heading/save.mjs +2 -6
- package/build-module/heading/save.mjs.map +2 -2
- package/build-module/heading/transforms.mjs +16 -3
- package/build-module/heading/transforms.mjs.map +2 -2
- package/build-module/image/image.mjs +3 -3
- package/build-module/image/image.mjs.map +2 -2
- package/build-module/image/index.mjs +33 -17
- package/build-module/image/index.mjs.map +2 -2
- package/build-module/list-item/index.mjs +3 -1
- package/build-module/list-item/index.mjs.map +2 -2
- package/build-module/media-text/index.mjs +30 -15
- package/build-module/media-text/index.mjs.map +2 -2
- package/build-module/more/block.json +1 -0
- package/build-module/more/index.mjs +3 -1
- package/build-module/more/index.mjs.map +2 -2
- package/build-module/navigation/edit/index.mjs +7 -6
- package/build-module/navigation/edit/index.mjs.map +2 -2
- package/build-module/navigation/edit/responsive-wrapper.mjs +7 -4
- package/build-module/navigation/edit/responsive-wrapper.mjs.map +2 -2
- package/build-module/navigation/edit/use-create-overlay.mjs +2 -0
- package/build-module/navigation/edit/use-create-overlay.mjs.map +2 -2
- package/build-module/navigation-link/edit.mjs +12 -60
- package/build-module/navigation-link/edit.mjs.map +2 -2
- package/build-module/navigation-link/index.mjs +13 -8
- package/build-module/navigation-link/index.mjs.map +2 -2
- package/build-module/navigation-link/shared/index.mjs +7 -1
- package/build-module/navigation-link/shared/index.mjs.map +2 -2
- package/build-module/navigation-link/shared/invalid-draft-display.mjs +47 -0
- package/build-module/navigation-link/shared/invalid-draft-display.mjs.map +7 -0
- package/build-module/navigation-link/shared/use-enable-link-status-validation.mjs +25 -0
- package/build-module/navigation-link/shared/use-enable-link-status-validation.mjs.map +7 -0
- package/build-module/navigation-link/shared/use-is-invalid-link.mjs +39 -0
- package/build-module/navigation-link/shared/use-is-invalid-link.mjs.map +7 -0
- package/build-module/navigation-overlay-close/block.json +1 -4
- package/build-module/navigation-overlay-close/index.mjs +3 -25
- package/build-module/navigation-overlay-close/index.mjs.map +2 -2
- package/build-module/navigation-submenu/edit.mjs +40 -19
- package/build-module/navigation-submenu/edit.mjs.map +2 -2
- package/build-module/navigation-submenu/index.mjs +15 -8
- package/build-module/navigation-submenu/index.mjs.map +2 -2
- package/build-module/nextpage/block.json +1 -0
- package/build-module/paragraph/index.mjs +3 -1
- package/build-module/paragraph/index.mjs.map +2 -2
- package/build-module/paragraph/transforms.mjs +2 -1
- package/build-module/paragraph/transforms.mjs.map +2 -2
- package/build-module/preformatted/index.mjs +3 -1
- package/build-module/preformatted/index.mjs.map +2 -2
- package/build-module/pullquote/index.mjs +7 -3
- package/build-module/pullquote/index.mjs.map +2 -2
- package/build-module/search/index.mjs +10 -5
- package/build-module/search/index.mjs.map +2 -2
- package/build-module/social-link/index.mjs +13 -7
- package/build-module/social-link/index.mjs.map +2 -2
- package/build-module/table-of-contents/hooks.mjs +1 -1
- package/build-module/table-of-contents/hooks.mjs.map +1 -1
- package/build-module/template-part/edit/utils/get-template-part-icon.mjs.map +1 -1
- package/build-module/utils/is-within-overlay.mjs +27 -0
- package/build-module/utils/is-within-overlay.mjs.map +7 -0
- package/build-module/verse/index.mjs +3 -1
- package/build-module/verse/index.mjs.map +2 -2
- package/build-module/video/index.mjs +20 -10
- package/build-module/video/index.mjs.map +2 -2
- package/build-style/navigation/style-rtl.css +1 -16
- package/build-style/navigation/style.css +1 -16
- package/build-style/style-rtl.css +1 -16
- package/build-style/style.css +1 -16
- package/package.json +37 -38
- package/src/audio/index.js +13 -7
- package/src/block-keyboard-shortcuts/index.js +4 -7
- package/src/breadcrumbs/index.php +6 -3
- package/src/button/index.js +15 -8
- package/src/code/index.js +2 -1
- package/src/comments-title/edit.js +10 -7
- package/src/comments-title/index.php +7 -8
- package/src/cover/index.js +17 -8
- package/src/details/index.js +2 -1
- package/src/file/index.js +15 -8
- package/src/heading/block.json +1 -3
- package/src/heading/deprecated.js +118 -5
- package/src/heading/edit.js +6 -32
- package/src/heading/edit.native.js +17 -2
- package/src/heading/index.js +2 -1
- package/src/heading/save.js +2 -11
- package/src/heading/transforms.js +16 -3
- package/src/image/image.js +38 -40
- package/src/image/index.js +29 -16
- package/src/list-item/index.js +2 -1
- package/src/media-text/index.js +27 -14
- package/src/more/block.json +1 -0
- package/src/more/index.js +2 -1
- package/src/navigation/edit/index.js +9 -4
- package/src/navigation/edit/responsive-wrapper.js +15 -8
- package/src/navigation/edit/test/use-create-overlay.js +12 -0
- package/src/navigation/edit/use-create-overlay.js +2 -0
- package/src/navigation/index.php +74 -24
- package/src/navigation/style.scss +2 -18
- package/src/navigation-link/edit.js +11 -97
- package/src/navigation-link/index.js +13 -8
- package/src/navigation-link/index.php +17 -29
- package/src/navigation-link/shared/helpers.php +46 -0
- package/src/navigation-link/shared/index.js +3 -0
- package/src/navigation-link/shared/invalid-draft-display.js +63 -0
- package/src/navigation-link/shared/test/use-enable-link-status-validation.test.js +127 -0
- package/src/navigation-link/shared/use-enable-link-status-validation.js +40 -0
- package/src/navigation-link/shared/use-is-invalid-link.js +72 -0
- package/src/navigation-overlay-close/block.json +1 -4
- package/src/navigation-overlay-close/index.js +2 -37
- package/src/navigation-overlay-close/index.php +37 -1
- package/src/navigation-submenu/edit.js +49 -24
- package/src/navigation-submenu/index.js +13 -8
- package/src/navigation-submenu/index.php +25 -18
- package/src/nextpage/block.json +1 -0
- package/src/paragraph/index.js +2 -1
- package/src/paragraph/transforms.js +3 -1
- package/src/preformatted/index.js +2 -1
- package/src/pullquote/index.js +5 -3
- package/src/query-title/index.php +2 -2
- package/src/search/index.js +7 -5
- package/src/social-link/index.js +12 -7
- package/src/table-of-contents/hooks.js +1 -1
- package/src/template-part/edit/utils/get-template-part-icon.js +1 -1
- package/src/utils/is-within-overlay.js +54 -0
- package/src/verse/index.js +2 -1
- package/src/video/index.js +17 -9
- package/build/navigation-overlay-close/save.cjs +0 -67
- package/build/navigation-overlay-close/save.cjs.map +0 -7
- package/build-module/navigation-overlay-close/save.mjs +0 -46
- package/build-module/navigation-overlay-close/save.mjs.map +0 -7
- package/src/navigation-overlay-close/save.js +0 -44
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { __ } from '@wordpress/i18n';
|
|
10
|
+
import { decodeEntities } from '@wordpress/html-entities';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Displays a label with an "(Invalid)" or "(Draft)" indicator for navigation links.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} props Component props.
|
|
16
|
+
* @param {string} props.label The label text to display.
|
|
17
|
+
* @param {boolean} props.isInvalid Whether the link is invalid (deleted or trashed).
|
|
18
|
+
* @param {boolean} props.isDraft Whether the link is a draft.
|
|
19
|
+
* @param {string} props.className Optional additional CSS class for the label element.
|
|
20
|
+
*
|
|
21
|
+
* @return {Element} The invalid/draft display component.
|
|
22
|
+
*/
|
|
23
|
+
export function InvalidDraftDisplay( {
|
|
24
|
+
label,
|
|
25
|
+
isInvalid,
|
|
26
|
+
isDraft,
|
|
27
|
+
className = 'wp-block-navigation-link__label',
|
|
28
|
+
} ) {
|
|
29
|
+
// Only render if the link is invalid or a draft.
|
|
30
|
+
if ( ! isInvalid && ! isDraft ) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const statusText = isInvalid
|
|
35
|
+
? /* translators: Indicating that the navigation link is Invalid. */
|
|
36
|
+
__( 'Invalid' )
|
|
37
|
+
: /* translators: Indicating that the navigation link is a Draft. */
|
|
38
|
+
__( 'Draft' );
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div
|
|
42
|
+
className={ clsx(
|
|
43
|
+
'wp-block-navigation-link__placeholder-text',
|
|
44
|
+
className,
|
|
45
|
+
{
|
|
46
|
+
'is-invalid': isInvalid,
|
|
47
|
+
'is-draft': isDraft,
|
|
48
|
+
}
|
|
49
|
+
) }
|
|
50
|
+
>
|
|
51
|
+
<span>
|
|
52
|
+
{
|
|
53
|
+
// Some attributes are stored in an escaped form. It's a legacy issue.
|
|
54
|
+
// Ideally they would be stored in a raw, unescaped form.
|
|
55
|
+
// Unescape is used here to "recover" the escaped characters
|
|
56
|
+
// so they display without encoding.
|
|
57
|
+
// See `updateAttributes` for more details.
|
|
58
|
+
`${ decodeEntities( label ) } (${ statusText })`.trim()
|
|
59
|
+
}
|
|
60
|
+
</span>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { renderHook } from '@testing-library/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { useEnableLinkStatusValidation } from '../use-enable-link-status-validation';
|
|
10
|
+
|
|
11
|
+
// Mock useSelect directly at the implementation level to avoid loading complex dependencies
|
|
12
|
+
jest.mock( '@wordpress/data/src/components/use-select', () => {
|
|
13
|
+
const mock = jest.fn();
|
|
14
|
+
return mock;
|
|
15
|
+
} );
|
|
16
|
+
|
|
17
|
+
const { useSelect } = require( '@wordpress/data' );
|
|
18
|
+
|
|
19
|
+
describe( 'useEnableLinkStatusValidation', () => {
|
|
20
|
+
const mockClientId = 'test-client-id';
|
|
21
|
+
const mockRootNavigationId = 'root-navigation-id';
|
|
22
|
+
const mockSelectedBlockId = 'selected-block-id';
|
|
23
|
+
|
|
24
|
+
beforeEach( () => {
|
|
25
|
+
jest.clearAllMocks();
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
it( 'should return true when root navigation block is selected', () => {
|
|
29
|
+
useSelect.mockImplementation( ( callback ) => {
|
|
30
|
+
return callback( () => ( {
|
|
31
|
+
getSelectedBlockClientId: () => mockRootNavigationId,
|
|
32
|
+
hasSelectedInnerBlock: () => false,
|
|
33
|
+
getBlockParentsByBlockName: () => [ mockRootNavigationId ],
|
|
34
|
+
} ) );
|
|
35
|
+
} );
|
|
36
|
+
|
|
37
|
+
const { result } = renderHook( () =>
|
|
38
|
+
useEnableLinkStatusValidation( mockClientId )
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
expect( result.current ).toBe( true );
|
|
42
|
+
} );
|
|
43
|
+
|
|
44
|
+
it( 'should return true when an inner block of root navigation is selected', () => {
|
|
45
|
+
useSelect.mockImplementation( ( callback ) => {
|
|
46
|
+
return callback( () => ( {
|
|
47
|
+
getSelectedBlockClientId: () => mockSelectedBlockId,
|
|
48
|
+
hasSelectedInnerBlock: ( clientId, deep ) =>
|
|
49
|
+
clientId === mockRootNavigationId && deep === true,
|
|
50
|
+
getBlockParentsByBlockName: () => [ mockRootNavigationId ],
|
|
51
|
+
} ) );
|
|
52
|
+
} );
|
|
53
|
+
|
|
54
|
+
const { result } = renderHook( () =>
|
|
55
|
+
useEnableLinkStatusValidation( mockClientId )
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect( result.current ).toBe( true );
|
|
59
|
+
} );
|
|
60
|
+
|
|
61
|
+
it( 'should return false when neither root navigation nor its inner blocks are selected', () => {
|
|
62
|
+
useSelect.mockImplementation( ( callback ) => {
|
|
63
|
+
return callback( () => ( {
|
|
64
|
+
getSelectedBlockClientId: () => 'other-block-id',
|
|
65
|
+
hasSelectedInnerBlock: () => false,
|
|
66
|
+
getBlockParentsByBlockName: () => [ mockRootNavigationId ],
|
|
67
|
+
} ) );
|
|
68
|
+
} );
|
|
69
|
+
|
|
70
|
+
const { result } = renderHook( () =>
|
|
71
|
+
useEnableLinkStatusValidation( mockClientId )
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
expect( result.current ).toBe( false );
|
|
75
|
+
} );
|
|
76
|
+
|
|
77
|
+
it( 'should handle case when root navigation id is not found', () => {
|
|
78
|
+
useSelect.mockImplementation( ( callback ) => {
|
|
79
|
+
return callback( () => ( {
|
|
80
|
+
getSelectedBlockClientId: () => mockSelectedBlockId,
|
|
81
|
+
hasSelectedInnerBlock: () => false,
|
|
82
|
+
getBlockParentsByBlockName: () => [],
|
|
83
|
+
} ) );
|
|
84
|
+
} );
|
|
85
|
+
|
|
86
|
+
const { result } = renderHook( () =>
|
|
87
|
+
useEnableLinkStatusValidation( mockClientId )
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// When rootNavigationId is undefined (empty array),
|
|
91
|
+
// both conditions will be false
|
|
92
|
+
expect( result.current ).toBe( false );
|
|
93
|
+
} );
|
|
94
|
+
|
|
95
|
+
it( 'should update when clientId changes', () => {
|
|
96
|
+
const newClientId = 'new-client-id';
|
|
97
|
+
const newRootNavigationId = 'new-root-navigation-id';
|
|
98
|
+
|
|
99
|
+
useSelect.mockImplementation( ( callback ) => {
|
|
100
|
+
return callback( () => ( {
|
|
101
|
+
getSelectedBlockClientId: () => newRootNavigationId,
|
|
102
|
+
hasSelectedInnerBlock: () => false,
|
|
103
|
+
getBlockParentsByBlockName: ( clientId ) => {
|
|
104
|
+
return clientId === newClientId
|
|
105
|
+
? [ newRootNavigationId ]
|
|
106
|
+
: [ mockRootNavigationId ];
|
|
107
|
+
},
|
|
108
|
+
} ) );
|
|
109
|
+
} );
|
|
110
|
+
|
|
111
|
+
const { result, rerender } = renderHook(
|
|
112
|
+
( { clientId } ) => useEnableLinkStatusValidation( clientId ),
|
|
113
|
+
{
|
|
114
|
+
initialProps: { clientId: mockClientId },
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Initially false (different IDs)
|
|
119
|
+
expect( result.current ).toBe( false );
|
|
120
|
+
|
|
121
|
+
// Rerender with new clientId
|
|
122
|
+
rerender( { clientId: newClientId } );
|
|
123
|
+
|
|
124
|
+
// Should now be true (matching IDs)
|
|
125
|
+
expect( result.current ).toBe( true );
|
|
126
|
+
} );
|
|
127
|
+
} );
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useSelect } from '@wordpress/data';
|
|
5
|
+
import { store as blockEditorStore } from '@wordpress/block-editor';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hook to determine if link status validation should be enabled.
|
|
9
|
+
*
|
|
10
|
+
* Link status validation is enabled when the root Navigation block is selected
|
|
11
|
+
* or any of its inner blocks are selected. This ensures validation only runs
|
|
12
|
+
* when the user is actively working within the navigation structure.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} clientId The client ID of the current block.
|
|
15
|
+
* @return {boolean} Whether link status validation should be enabled.
|
|
16
|
+
*/
|
|
17
|
+
export function useEnableLinkStatusValidation( clientId ) {
|
|
18
|
+
return useSelect(
|
|
19
|
+
( select ) => {
|
|
20
|
+
const {
|
|
21
|
+
getSelectedBlockClientId,
|
|
22
|
+
hasSelectedInnerBlock,
|
|
23
|
+
getBlockParentsByBlockName,
|
|
24
|
+
} = select( blockEditorStore );
|
|
25
|
+
|
|
26
|
+
const selectedBlockId = getSelectedBlockClientId();
|
|
27
|
+
const rootNavigationId = getBlockParentsByBlockName(
|
|
28
|
+
clientId,
|
|
29
|
+
'core/navigation'
|
|
30
|
+
)[ 0 ];
|
|
31
|
+
|
|
32
|
+
// Enable when the root Navigation block is selected or any of its inner blocks.
|
|
33
|
+
return (
|
|
34
|
+
selectedBlockId === rootNavigationId ||
|
|
35
|
+
hasSelectedInnerBlock( rootNavigationId, true )
|
|
36
|
+
);
|
|
37
|
+
},
|
|
38
|
+
[ clientId ]
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useSelect } from '@wordpress/data';
|
|
5
|
+
import { store as coreStore } from '@wordpress/core-data';
|
|
6
|
+
import { useBlockEditingMode } from '@wordpress/block-editor';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A React hook to determine if a navigation link or submenu's parent link is invalid.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} kind The kind of link (post-type, custom, taxonomy, etc).
|
|
12
|
+
* @param {string} type The type of post (post, page, etc).
|
|
13
|
+
* @param {number} id The post or term id.
|
|
14
|
+
* @param {boolean} enabled Whether to enable the validation check.
|
|
15
|
+
*
|
|
16
|
+
* @return {Array} Array containing [isInvalid, isDraft] booleans.
|
|
17
|
+
*/
|
|
18
|
+
export const useIsInvalidLink = ( kind, type, id, enabled ) => {
|
|
19
|
+
const isPostType =
|
|
20
|
+
kind === 'post-type' || type === 'post' || type === 'page';
|
|
21
|
+
const hasId = Number.isInteger( id );
|
|
22
|
+
const blockEditingMode = useBlockEditingMode();
|
|
23
|
+
|
|
24
|
+
const { postStatus, isDeleted } = useSelect(
|
|
25
|
+
( select ) => {
|
|
26
|
+
if ( ! isPostType ) {
|
|
27
|
+
return { postStatus: null, isDeleted: false };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Fetching the posts status is an "expensive" operation. Especially for sites with large navigations.
|
|
31
|
+
// When the block is rendered in a template or other disabled contexts we can skip this check in order
|
|
32
|
+
// to avoid all these additional requests that don't really add any value in that mode.
|
|
33
|
+
if ( blockEditingMode === 'disabled' || ! enabled ) {
|
|
34
|
+
return { postStatus: null, isDeleted: false };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const { getEntityRecord, hasFinishedResolution } =
|
|
38
|
+
select( coreStore );
|
|
39
|
+
const entityRecord = getEntityRecord( 'postType', type, id );
|
|
40
|
+
const hasResolved = hasFinishedResolution( 'getEntityRecord', [
|
|
41
|
+
'postType',
|
|
42
|
+
type,
|
|
43
|
+
id,
|
|
44
|
+
] );
|
|
45
|
+
|
|
46
|
+
// If resolution has finished and entityRecord is undefined, the entity was deleted.
|
|
47
|
+
const deleted = hasResolved && entityRecord === undefined;
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
postStatus: entityRecord?.status,
|
|
51
|
+
isDeleted: deleted,
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
[ isPostType, blockEditingMode, enabled, type, id ]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Check Navigation Link validity if:
|
|
58
|
+
// 1. Link is 'post-type'.
|
|
59
|
+
// 2. It has an id.
|
|
60
|
+
// 3. It's neither null, nor undefined, as valid items might be either of those while loading.
|
|
61
|
+
// If those conditions are met, check if
|
|
62
|
+
// 1. The post status is trash (trashed).
|
|
63
|
+
// 2. The entity doesn't exist (deleted).
|
|
64
|
+
// If either of those is true, invalidate.
|
|
65
|
+
const isInvalid =
|
|
66
|
+
isPostType &&
|
|
67
|
+
hasId &&
|
|
68
|
+
( isDeleted || ( postStatus && 'trash' === postStatus ) );
|
|
69
|
+
const isDraft = 'draft' === postStatus;
|
|
70
|
+
|
|
71
|
+
return [ isInvalid, isDraft ];
|
|
72
|
+
};
|
|
@@ -2,16 +2,14 @@
|
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { addFilter } from '@wordpress/hooks';
|
|
5
|
-
import { select } from '@wordpress/data';
|
|
6
|
-
import { store as coreStore } from '@wordpress/core-data';
|
|
7
5
|
|
|
8
6
|
/**
|
|
9
7
|
* Internal dependencies
|
|
10
8
|
*/
|
|
11
9
|
import initBlock from '../utils/init-block';
|
|
10
|
+
import { isWithinNavigationOverlay } from '../utils/is-within-overlay';
|
|
12
11
|
import edit from './edit';
|
|
13
12
|
import metadata from './block.json';
|
|
14
|
-
import save from './save';
|
|
15
13
|
import icon from './icon';
|
|
16
14
|
|
|
17
15
|
const { name } = metadata;
|
|
@@ -21,41 +19,8 @@ export { metadata, name };
|
|
|
21
19
|
export const settings = {
|
|
22
20
|
icon,
|
|
23
21
|
edit,
|
|
24
|
-
save,
|
|
25
22
|
};
|
|
26
23
|
|
|
27
|
-
function isWithinOverlay() {
|
|
28
|
-
// @wordpress/block-library should not depend on @wordpress/editor.
|
|
29
|
-
// Blocks can be loaded into a *non-post* block editor, so to avoid
|
|
30
|
-
// declaring @wordpress/editor as a dependency, we must access its
|
|
31
|
-
// store by string.
|
|
32
|
-
// eslint-disable-next-line @wordpress/data-no-store-string-literals
|
|
33
|
-
const editorStore = select( 'core/editor' );
|
|
34
|
-
|
|
35
|
-
// Return false if the editor store is not available.
|
|
36
|
-
if ( ! editorStore ) {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const { getCurrentPostType, getCurrentPostId } = editorStore;
|
|
41
|
-
const { getEditedEntityRecord } = select( coreStore );
|
|
42
|
-
|
|
43
|
-
const postType = getCurrentPostType();
|
|
44
|
-
const postId = getCurrentPostId();
|
|
45
|
-
|
|
46
|
-
if ( postType === 'wp_template_part' && postId ) {
|
|
47
|
-
const templatePartEntity = getEditedEntityRecord(
|
|
48
|
-
'postType',
|
|
49
|
-
'wp_template_part',
|
|
50
|
-
postId
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
return templatePartEntity?.area === 'overlay';
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
24
|
export const init = () => {
|
|
60
25
|
addFilter(
|
|
61
26
|
'blockEditor.__unstableCanInsertBlockType',
|
|
@@ -69,7 +34,7 @@ export const init = () => {
|
|
|
69
34
|
return canInsert;
|
|
70
35
|
}
|
|
71
36
|
|
|
72
|
-
return
|
|
37
|
+
return isWithinNavigationOverlay();
|
|
73
38
|
}
|
|
74
39
|
);
|
|
75
40
|
|
|
@@ -5,6 +5,39 @@
|
|
|
5
5
|
* @package WordPress
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Renders the `core/navigation-overlay-close` block on server.
|
|
10
|
+
*
|
|
11
|
+
* @param array $attributes The block attributes.
|
|
12
|
+
*
|
|
13
|
+
* @return string Returns the block content.
|
|
14
|
+
*/
|
|
15
|
+
function render_block_core_navigation_overlay_close( $attributes ) {
|
|
16
|
+
$text = empty( $attributes['text'] ) ? __( 'Close' ) : $attributes['text'];
|
|
17
|
+
$display_mode = empty( $attributes['displayMode'] ) ? 'icon' : $attributes['displayMode'];
|
|
18
|
+
$show_icon = 'both' === $display_mode || 'icon' === $display_mode;
|
|
19
|
+
$show_text = 'both' === $display_mode || 'text' === $display_mode;
|
|
20
|
+
$button_text = '';
|
|
21
|
+
|
|
22
|
+
if ( $show_icon ) {
|
|
23
|
+
$button_text .= '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false"><path d="M13 11.8l6.1-6.3-1.1-1-6.1 6.2-6.1-6.2-1.1 1 6.1 6.3-6.5 6.7 1.1 1 6.5-6.6 6.5 6.6 1.1-1z" /></svg>';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if ( $show_text ) {
|
|
27
|
+
$button_text .= '<span class="wp-block-navigation-overlay-close__text">' . wp_kses_post( $text ) . '</span>';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
$wrapper_attributes = get_block_wrapper_attributes();
|
|
31
|
+
$html_content = sprintf(
|
|
32
|
+
'<button %1$s type="button" %2$s >%3$s</button>',
|
|
33
|
+
$wrapper_attributes,
|
|
34
|
+
! $show_text ? 'aria-label="' . __( 'Close' ) . '"' : '',
|
|
35
|
+
$button_text
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return $html_content;
|
|
39
|
+
}
|
|
40
|
+
|
|
8
41
|
/**
|
|
9
42
|
* Registers the navigation overlay close block.
|
|
10
43
|
*
|
|
@@ -12,7 +45,10 @@
|
|
|
12
45
|
*/
|
|
13
46
|
function register_block_core_navigation_overlay_close() {
|
|
14
47
|
register_block_type_from_metadata(
|
|
15
|
-
__DIR__ . '/navigation-overlay-close'
|
|
48
|
+
__DIR__ . '/navigation-overlay-close',
|
|
49
|
+
array(
|
|
50
|
+
'render_callback' => 'render_block_core_navigation_overlay_close',
|
|
51
|
+
)
|
|
16
52
|
);
|
|
17
53
|
}
|
|
18
54
|
add_action( 'init', 'register_block_core_navigation_overlay_close' );
|
|
@@ -37,6 +37,9 @@ import {
|
|
|
37
37
|
LinkUI,
|
|
38
38
|
updateAttributes,
|
|
39
39
|
useEntityBinding,
|
|
40
|
+
useIsInvalidLink,
|
|
41
|
+
InvalidDraftDisplay,
|
|
42
|
+
useEnableLinkStatusValidation,
|
|
40
43
|
} from '../navigation-link/shared';
|
|
41
44
|
import {
|
|
42
45
|
getColors,
|
|
@@ -128,7 +131,7 @@ export default function NavigationSubmenuEdit( {
|
|
|
128
131
|
context,
|
|
129
132
|
clientId,
|
|
130
133
|
} ) {
|
|
131
|
-
const { label, url, description } = attributes;
|
|
134
|
+
const { label, url, description, kind, type, id } = attributes;
|
|
132
135
|
|
|
133
136
|
const {
|
|
134
137
|
showSubmenuIcon,
|
|
@@ -214,8 +217,18 @@ export default function NavigationSubmenuEdit( {
|
|
|
214
217
|
[ clientId ]
|
|
215
218
|
);
|
|
216
219
|
|
|
220
|
+
const validateLinkStatus = useEnableLinkStatusValidation( clientId );
|
|
221
|
+
|
|
217
222
|
const prevHasChildren = usePrevious( hasChildren );
|
|
218
223
|
|
|
224
|
+
// Check if the submenu's parent link is invalid or draft
|
|
225
|
+
const [ isInvalid, isDraft ] = useIsInvalidLink(
|
|
226
|
+
kind,
|
|
227
|
+
type,
|
|
228
|
+
id,
|
|
229
|
+
validateLinkStatus
|
|
230
|
+
);
|
|
231
|
+
|
|
219
232
|
// Show the LinkControl on mount if the URL is empty
|
|
220
233
|
// ( When adding a new menu item)
|
|
221
234
|
// This can't be done in the useState call because it conflicts
|
|
@@ -392,29 +405,41 @@ export default function NavigationSubmenuEdit( {
|
|
|
392
405
|
</InspectorControls>
|
|
393
406
|
<div { ...blockProps }>
|
|
394
407
|
<ParentElement className="wp-block-navigation-item__content">
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
{ description
|
|
417
|
-
|
|
408
|
+
{ ! isInvalid && ! isDraft && (
|
|
409
|
+
<>
|
|
410
|
+
<RichText
|
|
411
|
+
ref={ ref }
|
|
412
|
+
identifier="label"
|
|
413
|
+
className="wp-block-navigation-item__label"
|
|
414
|
+
value={ label }
|
|
415
|
+
onChange={ ( labelValue ) =>
|
|
416
|
+
setAttributes( { label: labelValue } )
|
|
417
|
+
}
|
|
418
|
+
onMerge={ mergeBlocks }
|
|
419
|
+
onReplace={ onReplace }
|
|
420
|
+
aria-label={ __( 'Navigation link text' ) }
|
|
421
|
+
placeholder={ itemLabelPlaceholder }
|
|
422
|
+
withoutInteractiveFormatting
|
|
423
|
+
onClick={ () => {
|
|
424
|
+
if ( ! openSubmenusOnClick && ! url ) {
|
|
425
|
+
setIsLinkOpen( true );
|
|
426
|
+
}
|
|
427
|
+
} }
|
|
428
|
+
/>
|
|
429
|
+
{ description && (
|
|
430
|
+
<span className="wp-block-navigation-item__description">
|
|
431
|
+
{ description }
|
|
432
|
+
</span>
|
|
433
|
+
) }
|
|
434
|
+
</>
|
|
435
|
+
) }
|
|
436
|
+
{ ( isInvalid || isDraft ) && (
|
|
437
|
+
<InvalidDraftDisplay
|
|
438
|
+
label={ label }
|
|
439
|
+
isInvalid={ isInvalid }
|
|
440
|
+
isDraft={ isDraft }
|
|
441
|
+
className="wp-block-navigation-item__label"
|
|
442
|
+
/>
|
|
418
443
|
) }
|
|
419
444
|
{ ! openSubmenusOnClick && isLinkOpen && (
|
|
420
445
|
<LinkUI
|
|
@@ -57,21 +57,26 @@ if ( window.__experimentalContentOnlyInspectorFields ) {
|
|
|
57
57
|
{
|
|
58
58
|
id: 'label',
|
|
59
59
|
label: __( 'Label' ),
|
|
60
|
-
type: '
|
|
60
|
+
type: 'text',
|
|
61
|
+
Edit: 'rich-text', //TODO: replace with custom component
|
|
61
62
|
},
|
|
62
63
|
{
|
|
63
64
|
id: 'link',
|
|
64
65
|
label: __( 'Link' ),
|
|
65
|
-
type: '
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
},
|
|
66
|
+
type: 'url',
|
|
67
|
+
Edit: 'link', // TODO: replace with custom component
|
|
68
|
+
getValue: ( { item } ) => ( {
|
|
69
|
+
url: item.url,
|
|
70
|
+
rel: item.rel,
|
|
71
|
+
} ),
|
|
72
|
+
setValue: ( { value } ) => ( {
|
|
73
|
+
url: value.url,
|
|
74
|
+
rel: value.rel,
|
|
75
|
+
} ),
|
|
71
76
|
},
|
|
72
77
|
];
|
|
73
78
|
settings[ formKey ] = {
|
|
74
|
-
fields: [ 'label' ],
|
|
79
|
+
fields: [ 'label', 'link' ],
|
|
75
80
|
};
|
|
76
81
|
}
|
|
77
82
|
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
* @package WordPress
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
// Path differs between source and build: '../navigation-link/shared/helpers.php' in source, './navigation-link/shared/helpers.php' in build.
|
|
9
|
+
if ( file_exists( __DIR__ . '/../navigation-link/shared/helpers.php' ) ) {
|
|
10
|
+
require_once __DIR__ . '/../navigation-link/shared/helpers.php';
|
|
11
|
+
} else {
|
|
12
|
+
require_once __DIR__ . '/navigation-link/shared/helpers.php';
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
/**
|
|
9
16
|
* Build an array with CSS classes and inline styles defining the font sizes
|
|
10
17
|
* which will be applied to the navigation markup in the front-end.
|
|
@@ -65,13 +72,11 @@ function block_core_navigation_submenu_render_submenu_icon() {
|
|
|
65
72
|
* @return string Returns the post content with the legacy widget added.
|
|
66
73
|
*/
|
|
67
74
|
function render_block_core_navigation_submenu( $attributes, $content, $block ) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if ( $is_post_type && $navigation_link_has_id && 'publish' !== get_post_status( $attributes['id'] ) ) {
|
|
74
|
-
return '';
|
|
75
|
+
// Check if this navigation item should render based on post status.
|
|
76
|
+
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
|
|
77
|
+
if ( ! gutenberg_block_core_shared_navigation_item_should_render( $attributes, $block ) ) {
|
|
78
|
+
return '';
|
|
79
|
+
}
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
// Don't render the block's subtree if it has no label.
|
|
@@ -82,9 +87,15 @@ function render_block_core_navigation_submenu( $attributes, $content, $block ) {
|
|
|
82
87
|
$font_sizes = block_core_navigation_submenu_build_css_font_sizes( $block->context );
|
|
83
88
|
$style_attribute = $font_sizes['inline_styles'];
|
|
84
89
|
|
|
85
|
-
|
|
86
|
-
$
|
|
87
|
-
|
|
90
|
+
// Render inner blocks first to check if any menu items will actually display.
|
|
91
|
+
$inner_blocks_html = '';
|
|
92
|
+
foreach ( $block->inner_blocks as $inner_block ) {
|
|
93
|
+
$inner_blocks_html .= $inner_block->render();
|
|
94
|
+
}
|
|
95
|
+
$has_submenu = ! empty( trim( $inner_blocks_html ) );
|
|
96
|
+
|
|
97
|
+
$kind = empty( $attributes['kind'] ) ? 'post_type' : str_replace( '-', '_', $attributes['kind'] );
|
|
98
|
+
$is_active = ! empty( $attributes['id'] ) && get_queried_object_id() === (int) $attributes['id'] && ! empty( get_queried_object()->$kind );
|
|
88
99
|
|
|
89
100
|
if ( is_post_type_archive() && ! empty( $attributes['url'] ) ) {
|
|
90
101
|
$queried_archive_link = get_post_type_archive_link( get_queried_object()->name );
|
|
@@ -190,7 +201,7 @@ function render_block_core_navigation_submenu( $attributes, $content, $block ) {
|
|
|
190
201
|
$html .= '</a>';
|
|
191
202
|
// End anchor tag content.
|
|
192
203
|
|
|
193
|
-
if ( $show_submenu_indicators ) {
|
|
204
|
+
if ( $show_submenu_indicators && $has_submenu ) {
|
|
194
205
|
// The submenu icon is rendered in a button here
|
|
195
206
|
// so that there's a clickable element to open the submenu.
|
|
196
207
|
$html .= '<button aria-label="' . esc_attr( $aria_label ) . '" class="wp-block-navigation__submenu-icon wp-block-navigation-submenu__toggle" aria-expanded="false">' . block_core_navigation_submenu_render_submenu_icon() . '</button>';
|
|
@@ -215,8 +226,9 @@ function render_block_core_navigation_submenu( $attributes, $content, $block ) {
|
|
|
215
226
|
|
|
216
227
|
$html .= '</button>';
|
|
217
228
|
|
|
218
|
-
|
|
219
|
-
|
|
229
|
+
if ( $has_submenu ) {
|
|
230
|
+
$html .= '<span class="wp-block-navigation__submenu-icon">' . block_core_navigation_submenu_render_submenu_icon() . '</span>';
|
|
231
|
+
}
|
|
220
232
|
}
|
|
221
233
|
|
|
222
234
|
if ( $has_submenu ) {
|
|
@@ -248,11 +260,6 @@ function render_block_core_navigation_submenu( $attributes, $content, $block ) {
|
|
|
248
260
|
$style_attribute = $colors_supports['style'];
|
|
249
261
|
}
|
|
250
262
|
|
|
251
|
-
$inner_blocks_html = '';
|
|
252
|
-
foreach ( $block->inner_blocks as $inner_block ) {
|
|
253
|
-
$inner_blocks_html .= $inner_block->render();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
263
|
if ( strpos( $inner_blocks_html, 'current-menu-item' ) ) {
|
|
257
264
|
$tag_processor = new WP_HTML_Tag_Processor( $html );
|
|
258
265
|
while ( $tag_processor->next_tag( array( 'class_name' => 'wp-block-navigation-item' ) ) ) {
|
package/src/nextpage/block.json
CHANGED
package/src/paragraph/index.js
CHANGED