@wordpress/editor 13.25.0 → 13.26.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 +6 -0
- package/LICENSE.md +1 -1
- package/build/components/document-bar/index.js +19 -7
- package/build/components/document-bar/index.js.map +1 -1
- package/build/components/document-outline/index.js +82 -1
- package/build/components/document-outline/index.js.map +1 -1
- package/build/components/document-tools/index.js +160 -0
- package/build/components/document-tools/index.js.map +1 -0
- package/build/components/editor-canvas/index.js +10 -4
- package/build/components/editor-canvas/index.js.map +1 -1
- package/build/components/entities-saved-states/index.js +3 -1
- package/build/components/entities-saved-states/index.js.map +1 -1
- package/build/components/global-keyboard-shortcuts/index.js +12 -2
- package/build/components/global-keyboard-shortcuts/index.js.map +1 -1
- package/build/components/global-keyboard-shortcuts/register-shortcuts.js +9 -0
- package/build/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
- package/build/components/index.js +56 -8
- package/build/components/index.js.map +1 -1
- package/build/components/index.native.js +9 -1
- package/build/components/index.native.js.map +1 -1
- package/build/components/inserter-sidebar/index.js +77 -0
- package/build/components/inserter-sidebar/index.js.map +1 -0
- package/build/components/list-view-sidebar/index.js +150 -0
- package/build/components/list-view-sidebar/index.js.map +1 -0
- package/build/components/list-view-sidebar/list-view-outline.js +28 -0
- package/build/components/list-view-sidebar/list-view-outline.js.map +1 -0
- package/build/components/offline-status/index.native.js +85 -0
- package/build/components/offline-status/index.native.js.map +1 -0
- package/build/components/page-attributes/panel.js +63 -0
- package/build/components/page-attributes/panel.js.map +1 -0
- package/build/components/post-discussion/panel.js +59 -0
- package/build/components/post-discussion/panel.js.map +1 -0
- package/build/components/post-excerpt/check.js +19 -0
- package/build/components/post-excerpt/check.js.map +1 -1
- package/build/components/post-excerpt/panel.js +55 -0
- package/build/components/post-excerpt/panel.js.map +1 -0
- package/build/components/post-excerpt/plugin.js +72 -0
- package/build/components/post-excerpt/plugin.js.map +1 -0
- package/build/components/post-featured-image/index.js +5 -8
- package/build/components/post-featured-image/index.js.map +1 -1
- package/build/components/post-featured-image/panel.js +60 -0
- package/build/components/post-featured-image/panel.js.map +1 -0
- package/build/components/post-last-revision/panel.js +27 -0
- package/build/components/post-last-revision/panel.js.map +1 -0
- package/build/components/post-saved-state/index.js +12 -8
- package/build/components/post-saved-state/index.js.map +1 -1
- package/build/components/post-taxonomies/panel.js +68 -0
- package/build/components/post-taxonomies/panel.js.map +1 -0
- package/build/components/post-template/block-theme.js +2 -1
- package/build/components/post-template/block-theme.js.map +1 -1
- package/build/components/post-template/hooks.js +6 -6
- package/build/components/post-template/hooks.js.map +1 -1
- package/build/components/post-template/panel.js +1 -2
- package/build/components/post-template/panel.js.map +1 -1
- package/build/components/post-template/swap-template-button.js +4 -2
- package/build/components/post-template/swap-template-button.js.map +1 -1
- package/build/components/post-title/index.native.js +25 -14
- package/build/components/post-title/index.native.js.map +1 -1
- package/build/components/post-view-link/index.js +58 -0
- package/build/components/post-view-link/index.js.map +1 -0
- package/build/components/post-visibility/check.js +5 -17
- package/build/components/post-visibility/check.js.map +1 -1
- package/build/components/preview-dropdown/index.js +8 -3
- package/build/components/preview-dropdown/index.js.map +1 -1
- package/build/components/provider/index.native.js +19 -0
- package/build/components/provider/index.native.js.map +1 -1
- package/build/components/provider/use-block-editor-settings.js +29 -5
- package/build/components/provider/use-block-editor-settings.js.map +1 -1
- package/build/private-apis.js +10 -0
- package/build/private-apis.js.map +1 -1
- package/build/store/actions.js +102 -2
- package/build/store/actions.js.map +1 -1
- package/build/store/index.js +2 -0
- package/build/store/index.js.map +1 -1
- package/build/store/private-selectors.js +52 -0
- package/build/store/private-selectors.js.map +1 -0
- package/build/store/reducer.js +78 -1
- package/build/store/reducer.js.map +1 -1
- package/build/store/selectors.js +76 -2
- package/build/store/selectors.js.map +1 -1
- package/build/utils/media-upload/index.js +8 -2
- package/build/utils/media-upload/index.js.map +1 -1
- package/build-module/components/document-bar/index.js +19 -7
- package/build-module/components/document-bar/index.js.map +1 -1
- package/build-module/components/document-outline/index.js +82 -1
- package/build-module/components/document-outline/index.js.map +1 -1
- package/build-module/components/document-tools/index.js +151 -0
- package/build-module/components/document-tools/index.js.map +1 -0
- package/build-module/components/editor-canvas/index.js +10 -4
- package/build-module/components/editor-canvas/index.js.map +1 -1
- package/build-module/components/entities-saved-states/index.js +3 -1
- package/build-module/components/entities-saved-states/index.js.map +1 -1
- package/build-module/components/global-keyboard-shortcuts/index.js +12 -2
- package/build-module/components/global-keyboard-shortcuts/index.js.map +1 -1
- package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js +9 -0
- package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
- package/build-module/components/index.js +6 -0
- package/build-module/components/index.js.map +1 -1
- package/build-module/components/index.native.js +1 -0
- package/build-module/components/index.native.js.map +1 -1
- package/build-module/components/inserter-sidebar/index.js +70 -0
- package/build-module/components/inserter-sidebar/index.js.map +1 -0
- package/build-module/components/list-view-sidebar/index.js +142 -0
- package/build-module/components/list-view-sidebar/index.js.map +1 -0
- package/build-module/components/list-view-sidebar/list-view-outline.js +20 -0
- package/build-module/components/list-view-sidebar/list-view-outline.js.map +1 -0
- package/build-module/components/offline-status/index.native.js +77 -0
- package/build-module/components/offline-status/index.native.js.map +1 -0
- package/build-module/components/page-attributes/panel.js +53 -0
- package/build-module/components/page-attributes/panel.js.map +1 -0
- package/build-module/components/post-discussion/panel.js +50 -0
- package/build-module/components/post-discussion/panel.js.map +1 -0
- package/build-module/components/post-excerpt/check.js +19 -0
- package/build-module/components/post-excerpt/check.js.map +1 -1
- package/build-module/components/post-excerpt/panel.js +48 -0
- package/build-module/components/post-excerpt/panel.js.map +1 -0
- package/build-module/components/post-excerpt/plugin.js +64 -0
- package/build-module/components/post-excerpt/plugin.js.map +1 -0
- package/build-module/components/post-featured-image/index.js +5 -8
- package/build-module/components/post-featured-image/index.js.map +1 -1
- package/build-module/components/post-featured-image/panel.js +51 -0
- package/build-module/components/post-featured-image/panel.js.map +1 -0
- package/build-module/components/post-last-revision/panel.js +18 -0
- package/build-module/components/post-last-revision/panel.js.map +1 -0
- package/build-module/components/post-saved-state/index.js +12 -8
- package/build-module/components/post-saved-state/index.js.map +1 -1
- package/build-module/components/post-taxonomies/panel.js +59 -0
- package/build-module/components/post-taxonomies/panel.js.map +1 -0
- package/build-module/components/post-template/block-theme.js +2 -1
- package/build-module/components/post-template/block-theme.js.map +1 -1
- package/build-module/components/post-template/hooks.js +6 -6
- package/build-module/components/post-template/hooks.js.map +1 -1
- package/build-module/components/post-template/panel.js +1 -2
- package/build-module/components/post-template/panel.js.map +1 -1
- package/build-module/components/post-template/swap-template-button.js +4 -2
- package/build-module/components/post-template/swap-template-button.js.map +1 -1
- package/build-module/components/post-title/index.native.js +26 -15
- package/build-module/components/post-title/index.native.js.map +1 -1
- package/build-module/components/post-view-link/index.js +51 -0
- package/build-module/components/post-view-link/index.js.map +1 -0
- package/build-module/components/post-visibility/check.js +6 -16
- package/build-module/components/post-visibility/check.js.map +1 -1
- package/build-module/components/preview-dropdown/index.js +8 -3
- package/build-module/components/preview-dropdown/index.js.map +1 -1
- package/build-module/components/provider/index.native.js +19 -0
- package/build-module/components/provider/index.native.js.map +1 -1
- package/build-module/components/provider/use-block-editor-settings.js +29 -5
- package/build-module/components/provider/use-block-editor-settings.js.map +1 -1
- package/build-module/private-apis.js +10 -0
- package/build-module/private-apis.js.map +1 -1
- package/build-module/store/actions.js +94 -0
- package/build-module/store/actions.js.map +1 -1
- package/build-module/store/index.js +2 -0
- package/build-module/store/index.js.map +1 -1
- package/build-module/store/private-selectors.js +43 -0
- package/build-module/store/private-selectors.js.map +1 -0
- package/build-module/store/reducer.js +74 -1
- package/build-module/store/reducer.js.map +1 -1
- package/build-module/store/selectors.js +67 -0
- package/build-module/store/selectors.js.map +1 -1
- package/build-module/utils/media-upload/index.js +8 -2
- package/build-module/utils/media-upload/index.js.map +1 -1
- package/build-style/style-rtl.css +251 -0
- package/build-style/style.css +251 -0
- package/package.json +32 -32
- package/src/components/document-bar/index.js +39 -28
- package/src/components/document-outline/index.js +48 -1
- package/src/components/document-outline/style.scss +12 -0
- package/src/components/document-tools/index.js +177 -0
- package/src/components/document-tools/style.scss +98 -0
- package/src/components/editor-canvas/index.js +12 -7
- package/src/components/editor-canvas/style.scss +5 -0
- package/src/components/entities-saved-states/index.js +3 -1
- package/src/components/entities-saved-states/style.scss +4 -0
- package/src/components/global-keyboard-shortcuts/index.js +12 -2
- package/src/components/global-keyboard-shortcuts/register-shortcuts.js +10 -0
- package/src/components/index.js +6 -0
- package/src/components/index.native.js +1 -0
- package/src/components/inserter-sidebar/index.js +73 -0
- package/src/components/inserter-sidebar/style.scss +22 -0
- package/src/components/list-view-sidebar/index.js +169 -0
- package/src/components/list-view-sidebar/list-view-outline.js +37 -0
- package/src/components/list-view-sidebar/style.scss +84 -0
- package/src/components/offline-status/index.native.js +101 -0
- package/src/components/offline-status/style.native.scss +28 -0
- package/src/components/offline-status/test/index.native.js +108 -0
- package/src/components/page-attributes/panel.js +62 -0
- package/src/components/post-discussion/panel.js +57 -0
- package/src/components/post-excerpt/check.js +18 -0
- package/src/components/post-excerpt/panel.js +57 -0
- package/src/components/post-excerpt/plugin.js +61 -0
- package/src/components/post-excerpt/test/plugin.js +36 -0
- package/src/components/post-featured-image/index.js +3 -7
- package/src/components/post-featured-image/panel.js +55 -0
- package/src/components/post-last-revision/panel.js +22 -0
- package/src/components/post-last-revision/style.scss +10 -0
- package/src/components/post-saved-state/index.js +8 -8
- package/src/components/post-taxonomies/panel.js +66 -0
- package/src/components/post-template/block-theme.js +2 -1
- package/src/components/post-template/hooks.js +6 -6
- package/src/components/post-template/panel.js +1 -2
- package/src/components/post-template/swap-template-button.js +7 -4
- package/src/components/post-title/index.native.js +32 -17
- package/src/components/post-title/style.scss +1 -0
- package/src/components/post-title/test/__snapshots__/index.native.js.snap +25 -0
- package/src/components/post-title/test/index.native.js +78 -0
- package/src/components/post-view-link/index.js +47 -0
- package/src/components/post-visibility/check.js +10 -15
- package/src/components/post-visibility/test/check.js +24 -13
- package/src/components/preview-dropdown/index.js +7 -10
- package/src/components/provider/index.native.js +29 -2
- package/src/components/provider/use-block-editor-settings.js +36 -8
- package/src/private-apis.js +10 -0
- package/src/store/actions.js +109 -0
- package/src/store/index.js +2 -0
- package/src/store/private-selectors.js +51 -0
- package/src/store/reducer.js +72 -0
- package/src/store/selectors.js +80 -0
- package/src/store/test/actions.js +56 -0
- package/src/store/test/reducer.js +98 -0
- package/src/store/test/selectors.js +49 -0
- package/src/style.scss +4 -0
- package/src/utils/media-upload/index.js +9 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useDispatch, useSelect } from '@wordpress/data';
|
|
5
|
+
import { Button, VisuallyHidden } from '@wordpress/components';
|
|
6
|
+
import { __experimentalLibrary as Library } from '@wordpress/block-editor';
|
|
7
|
+
import { close } from '@wordpress/icons';
|
|
8
|
+
import {
|
|
9
|
+
useViewportMatch,
|
|
10
|
+
__experimentalUseDialog as useDialog,
|
|
11
|
+
} from '@wordpress/compose';
|
|
12
|
+
import { __ } from '@wordpress/i18n';
|
|
13
|
+
import { useEffect, useRef } from '@wordpress/element';
|
|
14
|
+
import { store as preferencesStore } from '@wordpress/preferences';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Internal dependencies
|
|
18
|
+
*/
|
|
19
|
+
import { unlock } from '../../lock-unlock';
|
|
20
|
+
import { store as editorStore } from '../../store';
|
|
21
|
+
|
|
22
|
+
export default function InserterSidebar() {
|
|
23
|
+
const { insertionPoint, showMostUsedBlocks } = useSelect( ( select ) => {
|
|
24
|
+
const { getInsertionPoint } = unlock( select( editorStore ) );
|
|
25
|
+
const { get } = select( preferencesStore );
|
|
26
|
+
return {
|
|
27
|
+
insertionPoint: getInsertionPoint(),
|
|
28
|
+
showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ),
|
|
29
|
+
};
|
|
30
|
+
}, [] );
|
|
31
|
+
const { setIsInserterOpened } = useDispatch( editorStore );
|
|
32
|
+
|
|
33
|
+
const isMobileViewport = useViewportMatch( 'medium', '<' );
|
|
34
|
+
const TagName = ! isMobileViewport ? VisuallyHidden : 'div';
|
|
35
|
+
const [ inserterDialogRef, inserterDialogProps ] = useDialog( {
|
|
36
|
+
onClose: () => setIsInserterOpened( false ),
|
|
37
|
+
focusOnMount: null,
|
|
38
|
+
} );
|
|
39
|
+
|
|
40
|
+
const libraryRef = useRef();
|
|
41
|
+
useEffect( () => {
|
|
42
|
+
libraryRef.current.focusSearch();
|
|
43
|
+
}, [] );
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
ref={ inserterDialogRef }
|
|
48
|
+
{ ...inserterDialogProps }
|
|
49
|
+
className="editor-inserter-sidebar"
|
|
50
|
+
>
|
|
51
|
+
<TagName className="editor-inserter-sidebar__header">
|
|
52
|
+
<Button
|
|
53
|
+
icon={ close }
|
|
54
|
+
label={ __( 'Close block inserter' ) }
|
|
55
|
+
onClick={ () => setIsInserterOpened( false ) }
|
|
56
|
+
/>
|
|
57
|
+
</TagName>
|
|
58
|
+
<div className="editor-inserter-sidebar__content">
|
|
59
|
+
<Library
|
|
60
|
+
showMostUsedBlocks={ showMostUsedBlocks }
|
|
61
|
+
showInserterHelpPanel
|
|
62
|
+
shouldFocusBlock={ isMobileViewport }
|
|
63
|
+
rootClientId={ insertionPoint.rootClientId }
|
|
64
|
+
__experimentalInsertionIndex={
|
|
65
|
+
insertionPoint.insertionIndex
|
|
66
|
+
}
|
|
67
|
+
__experimentalFilterValue={ insertionPoint.filterValue }
|
|
68
|
+
ref={ libraryRef }
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.editor-inserter-sidebar {
|
|
2
|
+
@include reset;
|
|
3
|
+
|
|
4
|
+
height: 100%;
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.editor-inserter-sidebar__header {
|
|
10
|
+
padding-top: $grid-unit-10;
|
|
11
|
+
padding-right: $grid-unit-10;
|
|
12
|
+
display: flex;
|
|
13
|
+
justify-content: flex-end;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.editor-inserter-sidebar__content {
|
|
17
|
+
// Leave space for the close button
|
|
18
|
+
height: calc(100% - #{$button-size} - #{$grid-unit-10});
|
|
19
|
+
@include break-medium() {
|
|
20
|
+
height: 100%;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { __experimentalListView as ListView } from '@wordpress/block-editor';
|
|
5
|
+
import { Button, TabPanel } from '@wordpress/components';
|
|
6
|
+
import { useFocusOnMount, useMergeRefs } from '@wordpress/compose';
|
|
7
|
+
import { useDispatch, useSelect } from '@wordpress/data';
|
|
8
|
+
import { focus } from '@wordpress/dom';
|
|
9
|
+
import { useCallback, useRef, useState } from '@wordpress/element';
|
|
10
|
+
import { __, _x } from '@wordpress/i18n';
|
|
11
|
+
import { closeSmall } from '@wordpress/icons';
|
|
12
|
+
import { useShortcut } from '@wordpress/keyboard-shortcuts';
|
|
13
|
+
import { ESCAPE } from '@wordpress/keycodes';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Internal dependencies
|
|
17
|
+
*/
|
|
18
|
+
import ListViewOutline from './list-view-outline';
|
|
19
|
+
import { unlock } from '../../lock-unlock';
|
|
20
|
+
import { store as editorStore } from '../../store';
|
|
21
|
+
|
|
22
|
+
export default function ListViewSidebar() {
|
|
23
|
+
const { setIsListViewOpened } = useDispatch( editorStore );
|
|
24
|
+
const { getListViewToggleRef } = unlock( useSelect( editorStore ) );
|
|
25
|
+
|
|
26
|
+
// This hook handles focus when the sidebar first renders.
|
|
27
|
+
const focusOnMountRef = useFocusOnMount( 'firstElement' );
|
|
28
|
+
|
|
29
|
+
// When closing the list view, focus should return to the toggle button.
|
|
30
|
+
const closeListView = useCallback( () => {
|
|
31
|
+
setIsListViewOpened( false );
|
|
32
|
+
getListViewToggleRef().current?.focus();
|
|
33
|
+
}, [ getListViewToggleRef, setIsListViewOpened ] );
|
|
34
|
+
|
|
35
|
+
const closeOnEscape = useCallback(
|
|
36
|
+
( event ) => {
|
|
37
|
+
if ( event.keyCode === ESCAPE && ! event.defaultPrevented ) {
|
|
38
|
+
event.preventDefault();
|
|
39
|
+
closeListView();
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
[ closeListView ]
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Use internal state instead of a ref to make sure that the component
|
|
46
|
+
// re-renders when the dropZoneElement updates.
|
|
47
|
+
const [ dropZoneElement, setDropZoneElement ] = useState( null );
|
|
48
|
+
// Tracks our current tab.
|
|
49
|
+
const [ tab, setTab ] = useState( 'list-view' );
|
|
50
|
+
|
|
51
|
+
// This ref refers to the sidebar as a whole.
|
|
52
|
+
const sidebarRef = useRef();
|
|
53
|
+
// This ref refers to the tab panel.
|
|
54
|
+
const tabPanelRef = useRef();
|
|
55
|
+
// This ref refers to the list view application area.
|
|
56
|
+
const listViewRef = useRef();
|
|
57
|
+
|
|
58
|
+
// Must merge the refs together so focus can be handled properly in the next function.
|
|
59
|
+
const listViewContainerRef = useMergeRefs( [
|
|
60
|
+
focusOnMountRef,
|
|
61
|
+
listViewRef,
|
|
62
|
+
setDropZoneElement,
|
|
63
|
+
] );
|
|
64
|
+
|
|
65
|
+
/*
|
|
66
|
+
* Callback function to handle list view or outline focus.
|
|
67
|
+
*
|
|
68
|
+
* @param {string} currentTab The current tab. Either list view or outline.
|
|
69
|
+
*
|
|
70
|
+
* @return void
|
|
71
|
+
*/
|
|
72
|
+
function handleSidebarFocus( currentTab ) {
|
|
73
|
+
// Tab panel focus.
|
|
74
|
+
const tabPanelFocus = focus.tabbable.find( tabPanelRef.current )[ 0 ];
|
|
75
|
+
// List view tab is selected.
|
|
76
|
+
if ( currentTab === 'list-view' ) {
|
|
77
|
+
// Either focus the list view or the tab panel. Must have a fallback because the list view does not render when there are no blocks.
|
|
78
|
+
const listViewApplicationFocus = focus.tabbable.find(
|
|
79
|
+
listViewRef.current
|
|
80
|
+
)[ 0 ];
|
|
81
|
+
const listViewFocusArea = sidebarRef.current.contains(
|
|
82
|
+
listViewApplicationFocus
|
|
83
|
+
)
|
|
84
|
+
? listViewApplicationFocus
|
|
85
|
+
: tabPanelFocus;
|
|
86
|
+
listViewFocusArea.focus();
|
|
87
|
+
// Outline tab is selected.
|
|
88
|
+
} else {
|
|
89
|
+
tabPanelFocus.focus();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const handleToggleListViewShortcut = useCallback( () => {
|
|
94
|
+
// If the sidebar has focus, it is safe to close.
|
|
95
|
+
if (
|
|
96
|
+
sidebarRef.current.contains(
|
|
97
|
+
sidebarRef.current.ownerDocument.activeElement
|
|
98
|
+
)
|
|
99
|
+
) {
|
|
100
|
+
closeListView();
|
|
101
|
+
} else {
|
|
102
|
+
// If the list view or outline does not have focus, focus should be moved to it.
|
|
103
|
+
handleSidebarFocus( tab );
|
|
104
|
+
}
|
|
105
|
+
}, [ closeListView, tab ] );
|
|
106
|
+
|
|
107
|
+
// This only fires when the sidebar is open because of the conditional rendering.
|
|
108
|
+
// It is the same shortcut to open but that is defined as a global shortcut and only fires when the sidebar is closed.
|
|
109
|
+
useShortcut( 'core/editor/toggle-list-view', handleToggleListViewShortcut );
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Render tab content for a given tab name.
|
|
113
|
+
*
|
|
114
|
+
* @param {string} tabName The name of the tab to render.
|
|
115
|
+
*/
|
|
116
|
+
function renderTabContent( tabName ) {
|
|
117
|
+
if ( tabName === 'list-view' ) {
|
|
118
|
+
return (
|
|
119
|
+
<div className="editor-list-view-sidebar__list-view-panel-content">
|
|
120
|
+
<ListView dropZoneElement={ dropZoneElement } />
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return <ListViewOutline />;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
129
|
+
<div
|
|
130
|
+
className="editor-list-view-sidebar"
|
|
131
|
+
onKeyDown={ closeOnEscape }
|
|
132
|
+
ref={ sidebarRef }
|
|
133
|
+
>
|
|
134
|
+
<Button
|
|
135
|
+
className="editor-list-view-sidebar__close-button"
|
|
136
|
+
icon={ closeSmall }
|
|
137
|
+
label={ __( 'Close' ) }
|
|
138
|
+
onClick={ closeListView }
|
|
139
|
+
/>
|
|
140
|
+
<TabPanel
|
|
141
|
+
className="editor-list-view-sidebar__tab-panel"
|
|
142
|
+
ref={ tabPanelRef }
|
|
143
|
+
onSelect={ ( tabName ) => setTab( tabName ) }
|
|
144
|
+
selectOnMove={ false }
|
|
145
|
+
tabs={ [
|
|
146
|
+
{
|
|
147
|
+
name: 'list-view',
|
|
148
|
+
title: _x( 'List View', 'Post overview' ),
|
|
149
|
+
className: 'editor-list-view-sidebar__panel-tab',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'outline',
|
|
153
|
+
title: _x( 'Outline', 'Post overview' ),
|
|
154
|
+
className: 'editor-list-view-sidebar__panel-tab',
|
|
155
|
+
},
|
|
156
|
+
] }
|
|
157
|
+
>
|
|
158
|
+
{ ( currentTab ) => (
|
|
159
|
+
<div
|
|
160
|
+
className="editor-list-view-sidebar__list-view-container"
|
|
161
|
+
ref={ listViewContainerRef }
|
|
162
|
+
>
|
|
163
|
+
{ renderTabContent( currentTab.name ) }
|
|
164
|
+
</div>
|
|
165
|
+
) }
|
|
166
|
+
</TabPanel>
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { __experimentalText as Text } from '@wordpress/components';
|
|
5
|
+
import { __ } from '@wordpress/i18n';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import CharacterCount from '../character-count';
|
|
11
|
+
import WordCount from '../word-count';
|
|
12
|
+
import TimeToRead from '../time-to-read';
|
|
13
|
+
import DocumentOutline from '../document-outline';
|
|
14
|
+
|
|
15
|
+
export default function ListViewOutline() {
|
|
16
|
+
return (
|
|
17
|
+
<>
|
|
18
|
+
<div className="editor-list-view-sidebar__outline">
|
|
19
|
+
<div>
|
|
20
|
+
<Text>{ __( 'Characters:' ) }</Text>
|
|
21
|
+
<Text>
|
|
22
|
+
<CharacterCount />
|
|
23
|
+
</Text>
|
|
24
|
+
</div>
|
|
25
|
+
<div>
|
|
26
|
+
<Text>{ __( 'Words:' ) }</Text>
|
|
27
|
+
<WordCount />
|
|
28
|
+
</div>
|
|
29
|
+
<div>
|
|
30
|
+
<Text>{ __( 'Time to read:' ) }</Text>
|
|
31
|
+
<TimeToRead />
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<DocumentOutline />
|
|
35
|
+
</>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
.editor-list-view-sidebar {
|
|
2
|
+
height: 100%;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
|
|
6
|
+
@include break-medium() {
|
|
7
|
+
// Same width as the Inserter.
|
|
8
|
+
// @see packages/block-editor/src/components/inserter/style.scss
|
|
9
|
+
width: 350px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.editor-list-view-sidebar__close-button {
|
|
13
|
+
position: absolute;
|
|
14
|
+
right: $grid-unit-10;
|
|
15
|
+
top: math.div($grid-unit-60 - $button-size, 2); // ( tab height - button size ) / 2
|
|
16
|
+
z-index: 1;
|
|
17
|
+
background: $white;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// The TabPanel style overrides in the following blocks should be removed when the new TabPanel is available.
|
|
21
|
+
.components-tab-panel__tabs {
|
|
22
|
+
border-bottom: $border-width solid $gray-300;
|
|
23
|
+
box-sizing: border-box;
|
|
24
|
+
display: flex;
|
|
25
|
+
width: 100%;
|
|
26
|
+
padding-right: $grid-unit-70;
|
|
27
|
+
|
|
28
|
+
.editor-list-view-sidebar__panel-tab {
|
|
29
|
+
width: 50%;
|
|
30
|
+
margin-bottom: -$border-width;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.components-tab-panel__tab-content {
|
|
35
|
+
height: calc(100% - #{$grid-unit-60 - $border-width});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.editor-list-view-sidebar__list-view-panel-content,
|
|
40
|
+
.editor-list-view-sidebar__list-view-container > .document-outline {
|
|
41
|
+
height: 100%;
|
|
42
|
+
|
|
43
|
+
// Include custom scrollbars, invisible until hovered.
|
|
44
|
+
@include custom-scrollbars-on-hover(transparent, $gray-600);
|
|
45
|
+
overflow: auto;
|
|
46
|
+
|
|
47
|
+
// Only reserve space for scrollbars when there is content to scroll.
|
|
48
|
+
// This allows items in the list view to have equidistant padding left and right
|
|
49
|
+
// right up until a scrollbar is present.
|
|
50
|
+
scrollbar-gutter: auto;
|
|
51
|
+
|
|
52
|
+
// The table cells use an extra pixels of space left and right. We compensate for that here.
|
|
53
|
+
padding: $grid-unit-10 ($grid-unit-10 - $border-width - $border-width);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.editor-list-view-sidebar__list-view-container {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
height: 100%;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.editor-list-view-sidebar__tab-panel {
|
|
63
|
+
height: 100%;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.editor-list-view-sidebar__outline {
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
gap: $grid-unit-10;
|
|
70
|
+
border-bottom: $border-width solid $gray-300;
|
|
71
|
+
padding: $grid-unit-20;
|
|
72
|
+
|
|
73
|
+
& > div > span:first-child {
|
|
74
|
+
// Width of the text information fields.
|
|
75
|
+
width: 90px;
|
|
76
|
+
display: inline-block;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
& > div > span {
|
|
80
|
+
font-size: $helptext-font-size;
|
|
81
|
+
line-height: $default-line-height;
|
|
82
|
+
color: $gray-700;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { AccessibilityInfo, Text, View } from 'react-native';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
usePreferredColorSchemeStyle,
|
|
11
|
+
useNetworkConnectivity,
|
|
12
|
+
usePrevious,
|
|
13
|
+
} from '@wordpress/compose';
|
|
14
|
+
import { Icon } from '@wordpress/components';
|
|
15
|
+
import { offline as offlineIcon } from '@wordpress/icons';
|
|
16
|
+
import { __ } from '@wordpress/i18n';
|
|
17
|
+
import { useEffect } from '@wordpress/element';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Internal dependencies
|
|
21
|
+
*/
|
|
22
|
+
import styles from './style.native.scss';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Conditionally announces messages for screen reader users. This Hook provides
|
|
26
|
+
* two benefits over React Native's `accessibilityLiveRegion`:
|
|
27
|
+
*
|
|
28
|
+
* 1. It works on both iOS and Android.
|
|
29
|
+
* 2. It allows announcing a secondary message when the component is inactive.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} message The message to announce.
|
|
32
|
+
* @param {Object} options Options for the Hook.
|
|
33
|
+
* @param {boolean} [options.isActive] Whether the message should be announced.
|
|
34
|
+
* @param {string} [options.inactiveMessage] The message to announce when inactive.
|
|
35
|
+
*/
|
|
36
|
+
function useAccessibilityLiveRegion( message, { isActive, inactiveMessage } ) {
|
|
37
|
+
const { announceForAccessibility } = AccessibilityInfo;
|
|
38
|
+
const prevIsActive = usePrevious( isActive );
|
|
39
|
+
|
|
40
|
+
useEffect( () => {
|
|
41
|
+
const unconditionalMessage = typeof isActive === 'undefined';
|
|
42
|
+
const initialRender = typeof prevIsActive === 'undefined';
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
unconditionalMessage ||
|
|
46
|
+
( isActive && ! prevIsActive && ! initialRender )
|
|
47
|
+
) {
|
|
48
|
+
announceForAccessibility( message );
|
|
49
|
+
} else if ( ! isActive && prevIsActive && inactiveMessage ) {
|
|
50
|
+
announceForAccessibility( inactiveMessage );
|
|
51
|
+
}
|
|
52
|
+
}, [
|
|
53
|
+
message,
|
|
54
|
+
isActive,
|
|
55
|
+
prevIsActive,
|
|
56
|
+
inactiveMessage,
|
|
57
|
+
announceForAccessibility,
|
|
58
|
+
] );
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const OfflineStatus = () => {
|
|
62
|
+
const { isConnected } = useNetworkConnectivity();
|
|
63
|
+
|
|
64
|
+
useAccessibilityLiveRegion( __( 'Network connection re-established' ), {
|
|
65
|
+
isActive: isConnected,
|
|
66
|
+
inactiveMessage: __( 'Network connection lost, working offline' ),
|
|
67
|
+
} );
|
|
68
|
+
|
|
69
|
+
const containerStyle = usePreferredColorSchemeStyle(
|
|
70
|
+
styles.offline,
|
|
71
|
+
styles.offline__dark
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const textStyle = usePreferredColorSchemeStyle(
|
|
75
|
+
styles[ 'offline--text' ],
|
|
76
|
+
styles[ 'offline--text__dark' ]
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const iconStyle = usePreferredColorSchemeStyle(
|
|
80
|
+
styles[ 'offline--icon' ],
|
|
81
|
+
styles[ 'offline--icon__dark' ]
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return ! isConnected ? (
|
|
85
|
+
<View
|
|
86
|
+
accessible
|
|
87
|
+
accessibilityRole="alert"
|
|
88
|
+
accessibilityLabel={ __(
|
|
89
|
+
'Network connection lost, working offline'
|
|
90
|
+
) }
|
|
91
|
+
style={ containerStyle }
|
|
92
|
+
>
|
|
93
|
+
<View style={ containerStyle }>
|
|
94
|
+
<Icon fill={ iconStyle.fill } icon={ offlineIcon } />
|
|
95
|
+
<Text style={ textStyle }>{ __( 'Working Offline' ) }</Text>
|
|
96
|
+
</View>
|
|
97
|
+
</View>
|
|
98
|
+
) : null;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default OfflineStatus;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
.offline {
|
|
2
|
+
background-color: $gray-lighten-30;
|
|
3
|
+
padding: $grid-unit;
|
|
4
|
+
justify-content: center;
|
|
5
|
+
align-items: center;
|
|
6
|
+
flex-direction: row;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.offline__dark {
|
|
10
|
+
background-color: $gray-70;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.offline--text {
|
|
14
|
+
color: $black;
|
|
15
|
+
padding-left: 3;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.offline--text__dark {
|
|
19
|
+
color: $white;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.offline--icon {
|
|
23
|
+
fill: $gray-70;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.offline--icon__dark {
|
|
27
|
+
fill: $gray-10;
|
|
28
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { act, render, screen } from 'test/helpers';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
requestConnectionStatus,
|
|
11
|
+
subscribeConnectionStatus,
|
|
12
|
+
} from '@wordpress/react-native-bridge';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Internal dependencies
|
|
16
|
+
*/
|
|
17
|
+
import OfflineStatus from '../index';
|
|
18
|
+
import { AccessibilityInfo } from 'react-native';
|
|
19
|
+
|
|
20
|
+
jest.mock( '../style.native.scss', () => ( {
|
|
21
|
+
'offline--icon': {
|
|
22
|
+
fill: '',
|
|
23
|
+
},
|
|
24
|
+
} ) );
|
|
25
|
+
|
|
26
|
+
describe( 'when network connectivity is unavailable', () => {
|
|
27
|
+
beforeAll( () => {
|
|
28
|
+
requestConnectionStatus.mockImplementation( ( callback ) => {
|
|
29
|
+
callback( false );
|
|
30
|
+
return { remove: jest.fn() };
|
|
31
|
+
} );
|
|
32
|
+
} );
|
|
33
|
+
|
|
34
|
+
it( 'should display a helpful message', () => {
|
|
35
|
+
render( <OfflineStatus /> );
|
|
36
|
+
|
|
37
|
+
expect( screen.getByText( 'Working Offline' ) ).toBeVisible();
|
|
38
|
+
} );
|
|
39
|
+
|
|
40
|
+
it( 'should display an accessible message', () => {
|
|
41
|
+
render( <OfflineStatus /> );
|
|
42
|
+
|
|
43
|
+
expect(
|
|
44
|
+
screen.getByLabelText( 'Network connection lost, working offline' )
|
|
45
|
+
).toBeVisible();
|
|
46
|
+
} );
|
|
47
|
+
|
|
48
|
+
it( 'should announce network status', () => {
|
|
49
|
+
render( <OfflineStatus /> );
|
|
50
|
+
|
|
51
|
+
expect(
|
|
52
|
+
AccessibilityInfo.announceForAccessibility
|
|
53
|
+
).toHaveBeenCalledWith( 'Network connection lost, working offline' );
|
|
54
|
+
} );
|
|
55
|
+
|
|
56
|
+
it( 'should announce changes to network status', () => {
|
|
57
|
+
let subscriptionCallback;
|
|
58
|
+
subscribeConnectionStatus.mockImplementation( ( callback ) => {
|
|
59
|
+
subscriptionCallback = callback;
|
|
60
|
+
return { remove: jest.fn() };
|
|
61
|
+
} );
|
|
62
|
+
render( <OfflineStatus /> );
|
|
63
|
+
|
|
64
|
+
act( () => subscriptionCallback( { isConnected: false } ) );
|
|
65
|
+
|
|
66
|
+
expect(
|
|
67
|
+
AccessibilityInfo.announceForAccessibility
|
|
68
|
+
).toHaveBeenCalledWith( 'Network connection lost, working offline' );
|
|
69
|
+
} );
|
|
70
|
+
} );
|
|
71
|
+
|
|
72
|
+
describe( 'when network connectivity is available', () => {
|
|
73
|
+
beforeAll( () => {
|
|
74
|
+
requestConnectionStatus.mockImplementation( ( callback ) => {
|
|
75
|
+
callback( true );
|
|
76
|
+
return { remove: jest.fn() };
|
|
77
|
+
} );
|
|
78
|
+
} );
|
|
79
|
+
|
|
80
|
+
it( 'should not display a helpful message', () => {
|
|
81
|
+
render( <OfflineStatus /> );
|
|
82
|
+
|
|
83
|
+
expect( screen.queryByText( 'Working Offline' ) ).toBeNull();
|
|
84
|
+
} );
|
|
85
|
+
|
|
86
|
+
it( 'should not announce network status', () => {
|
|
87
|
+
render( <OfflineStatus /> );
|
|
88
|
+
|
|
89
|
+
expect(
|
|
90
|
+
AccessibilityInfo.announceForAccessibility
|
|
91
|
+
).not.toHaveBeenCalled();
|
|
92
|
+
} );
|
|
93
|
+
|
|
94
|
+
it( 'should announce changes to network status', () => {
|
|
95
|
+
let subscriptionCallback;
|
|
96
|
+
subscribeConnectionStatus.mockImplementation( ( callback ) => {
|
|
97
|
+
subscriptionCallback = callback;
|
|
98
|
+
return { remove: jest.fn() };
|
|
99
|
+
} );
|
|
100
|
+
render( <OfflineStatus /> );
|
|
101
|
+
|
|
102
|
+
act( () => subscriptionCallback( { isConnected: false } ) );
|
|
103
|
+
|
|
104
|
+
expect(
|
|
105
|
+
AccessibilityInfo.announceForAccessibility
|
|
106
|
+
).toHaveBeenCalledWith( 'Network connection lost, working offline' );
|
|
107
|
+
} );
|
|
108
|
+
} );
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
import { PanelBody, PanelRow } from '@wordpress/components';
|
|
6
|
+
|
|
7
|
+
import { useSelect, useDispatch } from '@wordpress/data';
|
|
8
|
+
import { store as coreStore } from '@wordpress/core-data';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Internal dependencies
|
|
12
|
+
*/
|
|
13
|
+
import { store as editorStore } from '../../store';
|
|
14
|
+
import PageAttributesCheck from './check';
|
|
15
|
+
import PageAttributesOrder from './order';
|
|
16
|
+
import PageAttributesParent from './parent';
|
|
17
|
+
|
|
18
|
+
const PANEL_NAME = 'page-attributes';
|
|
19
|
+
|
|
20
|
+
export function PageAttributesPanel() {
|
|
21
|
+
const { isEnabled, isOpened, postType } = useSelect( ( select ) => {
|
|
22
|
+
const {
|
|
23
|
+
getEditedPostAttribute,
|
|
24
|
+
isEditorPanelEnabled,
|
|
25
|
+
isEditorPanelOpened,
|
|
26
|
+
} = select( editorStore );
|
|
27
|
+
const { getPostType } = select( coreStore );
|
|
28
|
+
return {
|
|
29
|
+
isEnabled: isEditorPanelEnabled( PANEL_NAME ),
|
|
30
|
+
isOpened: isEditorPanelOpened( PANEL_NAME ),
|
|
31
|
+
postType: getPostType( getEditedPostAttribute( 'type' ) ),
|
|
32
|
+
};
|
|
33
|
+
}, [] );
|
|
34
|
+
|
|
35
|
+
const { toggleEditorPanelOpened } = useDispatch( editorStore );
|
|
36
|
+
|
|
37
|
+
if ( ! isEnabled || ! postType ) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const onTogglePanel = ( ...args ) =>
|
|
42
|
+
toggleEditorPanelOpened( PANEL_NAME, ...args );
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<PageAttributesCheck>
|
|
46
|
+
<PanelBody
|
|
47
|
+
title={
|
|
48
|
+
postType?.labels?.attributes ?? __( 'Page attributes' )
|
|
49
|
+
}
|
|
50
|
+
opened={ isOpened }
|
|
51
|
+
onToggle={ onTogglePanel }
|
|
52
|
+
>
|
|
53
|
+
<PageAttributesParent />
|
|
54
|
+
<PanelRow>
|
|
55
|
+
<PageAttributesOrder />
|
|
56
|
+
</PanelRow>
|
|
57
|
+
</PanelBody>
|
|
58
|
+
</PageAttributesCheck>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default PageAttributesPanel;
|